<commit_before>package sched

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

const (
	Asterisk = "*"
	Question = "?"
	Hyphen   = "-"
	Slash    = "/"
	Comma    = ","
	Hash     = "#"
	Last     = "L"
	Weekday  = "W"

	FieldSeparators = " \t"
	TrimCutset      = FieldSeparators + "\n"
)

type ParseError struct {
	Expression  string
	Description string
}

func newParseError(exp, desc string) *ParseError {
	return &ParseError{
		Expression:  exp,
		Description: desc,
	}
}

func (p *ParseError) Error() string {
	return fmt.Sprintf("sched: could not parse %q: %v", p.Expression, p.Description)
}

var fieldSeparatorFunc = func(r rune) bool {
	return strings.ContainsRune(FieldSeparators, r)
}

func Fields(expression string) []string {
	return strings.FieldsFunc(strings.Trim(expression, TrimCutset), fieldSeparatorFunc)
}

func FieldParts(field string) []string {
	result := strings.Split(field, Comma)
	if len(result) == 1 && len(result[0]) == 0 {
		return []string{}
	}
	return result
}

func MustParse(expression string) Schedule {
	s, err := Parse(expression)
	if err != nil {
		panic(err)
	}
	return s
}

func Parse(expression string) (Schedule, error) {
	//ParseErrors should be returned from this function and no others.
	fieldStrings, err := getNormalizedFields(expression)
	if err != nil {
		return nil, newParseError(expression, err.Error())
	}
	s := newSchedule()
	for i, fieldString := range fieldStrings {
		fi := fieldIndex(i)
		nexter, err := parseField(fieldString, fi)
		if err != nil {
			return nil, newParseError(expression, err.Error())
		}
		s.setNexter(nexter, fi)
	}
	return s, nil
}

func getNormalizedFields(expression string) ([]string, error) {
	fields := Fields(expression)
	var err error = nil
	count, err := validateNumberOfFields(fields)
	if err != nil {
		return nil, err
	}
	if count == 1 {
		fields, err = getNormalizedDirectiveFields(fields[0])
		if err != nil {
			return nil, err
		}
	}
	if count == 5 {
		fields = append([]string{Asterisk}, fields...)
		count++
	}
	if count == 6 {
		fields = append(fields, Asterisk)
		count++
	}
	return fields, nil
}

func validateNumberOfFields(fields []string) (int, error) {
	count := len(fields)
	if count != 1 && count != 5 && count != 6 && count != 7 {
		return 0, fmt.Errorf("number of fields must be 1, 5, 6, or 7")
	}
	return count, nil
}

func getNormalizedDirectiveFields(directive string) ([]string, error) {
	format := ""
	directive = strings.ToLower(directive)
	switch directive {
	case Yearly:
		format = YearlyFormat
	case Annually:
		format = AnnuallyFormat
	case Monthly:
		format = MonthlyFormat
	case Weekly:
		format = WeeklyFormat
	case Daily:
		format = DailyFormat
	case Hourly:
		format = HourlyFormat
	case Minutely:
		format = MinutelyFormat
	case Secondly:
		format = SecondlyFormat
	}
	if format == "" {
		return nil, fmt.Errorf("the directive %q is not recognized", directive)
	}
	return Fields(format), nil
}

func parseField(field string, fi fieldIndex) (nexter interface{}, err error) {
	parts := FieldParts(field)
	if fi.isDateField() {
		nexter, err = parseDateFieldNexterParts(parts, fi)
	} else {
		nexter, err = parseFieldNexterParts(parts, fi)
	}
	if err != nil {
		err = fmt.Errorf("%v field: %v", fi, err.Error())
		nexter = nil
	}
	return
}

func parseDateFieldNexterParts(parts []string, fi fieldIndex) (dateFieldNexter, error) {
	return nil, nil
}

func parseFieldNexterParts(parts []string, fi fieldIndex) (fieldNexter, error) {
	return nil, nil
}

func parseFieldNexterPart(part string, fi fieldIndex) (fieldNexter, error) {
	if len(part) == 0 {
		return nil, fmt.Errorf("cannot be empty")
	}
	slashIndex := strings.Index(part, Slash)
	if slashIndex < 0 {
		return parseRangeOrConstantNexter(part[:slashIndex], fi)
	}
	if slashIndex == 0 {
		return nil, fmt.Errorf("value before step cannot be empty")
	}
	rn, err := parseRangeNexter(part[:slashIndex], fi)
	if err != nil {
		return nil, err
	}
	inc, err := parseIncValue(part[slashIndex+1:])
	if err != nil {
		return nil, err
	}
	return newRangeDivNexter(rn, inc), nil
}

func parseRangeOrConstantNexter(part string, fi fieldIndex) (fieldNexter, error) {
	part = convertPossibleAnyToRange(part, fi)
	if strings.Contains(part, Hyphen) {
		return parseRangeNexter(part, fi)
	}
	return parseValueNexter(part, fi)
}

func convertPossibleAnyToRange(part string, fi fieldIndex) string {
	rangeString := fi.rangeString()
	if fi.isDateField() {
		part = strings.Replace(part, Question, Asterisk, -1)
	}
	return strings.Replace(part, Asterisk, rangeString, -1)
}

//parseRangeNexter will panic if part does not conatin Hyphen.
func parseRangeNexter(part string, fi fieldIndex) (*rangeNexter, error) {
	hyphenIndex := strings.Index(part, Hyphen)
	min, err := parseSingleValue(part[:hyphenIndex], fi)
	if err != nil {
		return nil, fmt.Errorf("left side of range: %v", err.Error())
	}
	max, err := parseSingleValue(part[hyphenIndex+1:], fi)
	if err != nil {
		return nil, fmt.Errorf("right side of range: %v", err.Error())
	}
	if max <= min {
		return nil, fmt.Errorf("left side of range must be strictly less than right side")
	}
	return newRangeNexter(min, max), nil
}

func parseValueNexter(part string, fi fieldIndex) (valueNexter, error) {
	value, err := parseSingleValue(part, fi)
	if err != nil {
		return valueNexter(invalidValue), err
	}
	return valueNexter(value), nil
}

func parseSingleValue(value string, fi fieldIndex) (int, error) {
	value = convertPossibleMonthDowToInteger(value, fi)
	result, err := strconv.Atoi(value)
	if err != nil {
		if fi.isDateField() {
			return invalidValue, fmt.Errorf("must be a decimal integer or valid string alias")
		}
		return invalidValue, fmt.Errorf("must be a decimal integer")
	}
	if !fi.isInRange(result) {
		return invalidValue, fmt.Errorf("not in range")
	}
	return result, nil
}

func convertPossibleMonthDowToInteger(value string, fi fieldIndex) string {
	if fi == month {
		return convertMonthToInteger(value)
	}
	if fi == dow {
		return converDowToInteger(value)
	}
	return value
}

func convertMonthToInteger(value string) string {
	if len(value) < 3 {
		return value
	}
	value = strings.ToUpper(value)
	for m := time.January; m <= time.December; m++ {
		if strings.HasPrefix(strings.ToUpper(fmt.Sprint(m)), value) {
			return fmt.Sprint(int(m))
		}
	}
	return value
}

func converDowToInteger(value string) string {
	if len(value) < 3 {
		return value
	}
	value = strings.ToUpper(value)
	for w := time.Sunday; w <= time.Saturday; w++ {
		if strings.HasPrefix(strings.ToUpper(fmt.Sprint(w)), value) {
			return fmt.Sprint(int(w))
		}
	}
	return value
}

func parseIncValue(value string) (int, error) {
	if len(value) == 0 {
		return invalidValue, fmt.Errorf("step value cannot be empty")
	}
	inc, err := strconv.Atoi(value)
	if err != nil || inc <= 0 {
		return invalidValue, fmt.Errorf("step value must be a positive decimal integer")
	}
	return inc, nil
}
<commit_msg>Adds parseFieldNexterParts implementation.<commit_after>package sched

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

const (
	Asterisk = "*"
	Question = "?"
	Hyphen   = "-"
	Slash    = "/"
	Comma    = ","
	Hash     = "#"
	Last     = "L"
	Weekday  = "W"

	FieldSeparators = " \t"
	TrimCutset      = FieldSeparators + "\n"
)

type ParseError struct {
	Expression  string
	Description string
}

func newParseError(exp, desc string) *ParseError {
	return &ParseError{
		Expression:  exp,
		Description: desc,
	}
}

func (p *ParseError) Error() string {
	return fmt.Sprintf("sched: could not parse %q: %v", p.Expression, p.Description)
}

var fieldSeparatorFunc = func(r rune) bool {
	return strings.ContainsRune(FieldSeparators, r)
}

func Fields(expression string) []string {
	return strings.FieldsFunc(strings.Trim(expression, TrimCutset), fieldSeparatorFunc)
}

func FieldParts(field string) []string {
	result := strings.Split(field, Comma)
	if len(result) == 1 && len(result[0]) == 0 {
		return []string{}
	}
	return result
}

func MustParse(expression string) Schedule {
	s, err := Parse(expression)
	if err != nil {
		panic(err)
	}
	return s
}

func Parse(expression string) (Schedule, error) {
	//ParseErrors should be returned from this function and no others.
	fieldStrings, err := getNormalizedFields(expression)
	if err != nil {
		return nil, newParseError(expression, err.Error())
	}
	s := newSchedule()
	for i, fieldString := range fieldStrings {
		fi := fieldIndex(i)
		nexter, err := parseField(fieldString, fi)
		if err != nil {
			return nil, newParseError(expression, err.Error())
		}
		s.setNexter(nexter, fi)
	}
	return s, nil
}

func getNormalizedFields(expression string) ([]string, error) {
	fields := Fields(expression)
	var err error = nil
	count, err := validateNumberOfFields(fields)
	if err != nil {
		return nil, err
	}
	if count == 1 {
		fields, err = getNormalizedDirectiveFields(fields[0])
		if err != nil {
			return nil, err
		}
	}
	if count == 5 {
		fields = append([]string{Asterisk}, fields...)
		count++
	}
	if count == 6 {
		fields = append(fields, Asterisk)
		count++
	}
	return fields, nil
}

func validateNumberOfFields(fields []string) (int, error) {
	count := len(fields)
	if count != 1 && count != 5 && count != 6 && count != 7 {
		return 0, fmt.Errorf("number of fields must be 1, 5, 6, or 7")
	}
	return count, nil
}

func getNormalizedDirectiveFields(directive string) ([]string, error) {
	format := ""
	directive = strings.ToLower(directive)
	switch directive {
	case Yearly:
		format = YearlyFormat
	case Annually:
		format = AnnuallyFormat
	case Monthly:
		format = MonthlyFormat
	case Weekly:
		format = WeeklyFormat
	case Daily:
		format = DailyFormat
	case Hourly:
		format = HourlyFormat
	case Minutely:
		format = MinutelyFormat
	case Secondly:
		format = SecondlyFormat
	}
	if format == "" {
		return nil, fmt.Errorf("the directive %q is not recognized", directive)
	}
	return Fields(format), nil
}

func parseField(field string, fi fieldIndex) (nexter interface{}, err error) {
	parts := FieldParts(field)
	if fi.isDateField() {
		nexter, err = parseDateFieldNexterParts(parts, fi)
	} else {
		nexter, err = parseFieldNexterParts(parts, fi)
	}
	if err != nil {
		err = fmt.Errorf("%v field: %v", fi, err.Error())
		nexter = nil
	}
	return
}

func parseDateFieldNexterParts(parts []string, fi fieldIndex) (dateFieldNexter, error) {
	return nil, nil
}

func parseFieldNexterParts(parts []string, fi fieldIndex) (fieldNexter, error) {
	if len(parts) == 1 {
		return parseFieldNexterPart(parts[0], fi)
	}
	result := multiNexter(make([]fieldNexter, 0, len(parts)))
	for i, part := range parts {
		nexter, err := parseFieldNexterPart(part, fi)
		if err != nil {
			return nil, newPartError(i, err)
		}
		result = append(result, nexter)
	}
	return result, nil
}

func newPartError(index int, old error) error {
	return fmt.Errorf("part %v: %v", index+1, old.Error())
}

func parseFieldNexterPart(part string, fi fieldIndex) (fieldNexter, error) {
	if len(part) == 0 {
		return nil, fmt.Errorf("cannot be empty")
	}
	slashIndex := strings.Index(part, Slash)
	if slashIndex < 0 {
		return parseRangeOrConstantNexter(part[:slashIndex], fi)
	}
	if slashIndex == 0 {
		return nil, fmt.Errorf("value before step cannot be empty")
	}
	rn, err := parseRangeNexter(part[:slashIndex], fi)
	if err != nil {
		return nil, err
	}
	inc, err := parseIncValue(part[slashIndex+1:])
	if err != nil {
		return nil, err
	}
	return newRangeDivNexter(rn, inc), nil
}

func parseRangeOrConstantNexter(part string, fi fieldIndex) (fieldNexter, error) {
	part = convertPossibleAnyToRange(part, fi)
	if strings.Contains(part, Hyphen) {
		return parseRangeNexter(part, fi)
	}
	return parseValueNexter(part, fi)
}

func convertPossibleAnyToRange(part string, fi fieldIndex) string {
	rangeString := fi.rangeString()
	if fi.isDateField() {
		part = strings.Replace(part, Question, Asterisk, -1)
	}
	return strings.Replace(part, Asterisk, rangeString, -1)
}

//parseRangeNexter will panic if part does not conatin Hyphen.
func parseRangeNexter(part string, fi fieldIndex) (*rangeNexter, error) {
	hyphenIndex := strings.Index(part, Hyphen)
	min, err := parseSingleValue(part[:hyphenIndex], fi)
	if err != nil {
		return nil, fmt.Errorf("left side of range: %v", err.Error())
	}
	max, err := parseSingleValue(part[hyphenIndex+1:], fi)
	if err != nil {
		return nil, fmt.Errorf("right side of range: %v", err.Error())
	}
	if min >= max {
		return nil, fmt.Errorf("left side of range must be strictly less than right side")
	}
	return newRangeNexter(min, max), nil
}

func parseValueNexter(part string, fi fieldIndex) (valueNexter, error) {
	value, err := parseSingleValue(part, fi)
	if err != nil {
		return valueNexter(invalidValue), err
	}
	return valueNexter(value), nil
}

func parseSingleValue(value string, fi fieldIndex) (int, error) {
	value = convertPossibleMonthDowToInteger(value, fi)
	result, err := strconv.Atoi(value)
	if err != nil {
		if fi.isDateField() {
			return invalidValue, fmt.Errorf("must be a decimal integer or valid string alias")
		}
		return invalidValue, fmt.Errorf("must be a decimal integer")
	}
	if !fi.isInRange(result) {
		return invalidValue, fmt.Errorf("not in range")
	}
	return result, nil
}

func convertPossibleMonthDowToInteger(value string, fi fieldIndex) string {
	if fi == month {
		return convertMonthToInteger(value)
	}
	if fi == dow {
		return converDowToInteger(value)
	}
	return value
}

func convertMonthToInteger(value string) string {
	if len(value) < 3 {
		return value
	}
	value = strings.ToUpper(value)
	for m := time.January; m <= time.December; m++ {
		if strings.HasPrefix(strings.ToUpper(fmt.Sprint(m)), value) {
			return fmt.Sprint(int(m))
		}
	}
	return value
}

func converDowToInteger(value string) string {
	if len(value) < 3 {
		return value
	}
	value = strings.ToUpper(value)
	for w := time.Sunday; w <= time.Saturday; w++ {
		if strings.HasPrefix(strings.ToUpper(fmt.Sprint(w)), value) {
			return fmt.Sprint(int(w))
		}
	}
	return value
}

func parseIncValue(value string) (int, error) {
	if len(value) == 0 {
		return invalidValue, fmt.Errorf("step value cannot be empty")
	}
	inc, err := strconv.Atoi(value)
	if err != nil || inc <= 0 {
		return invalidValue, fmt.Errorf("step value must be a positive decimal integer")
	}
	return inc, nil
}
<|endoftext|>


<commit_before>package push

import (
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("push with --strategy rolling", func() {
	var (
		appName  string
		userName string
	)

	BeforeEach(func() {
		appName = helpers.PrefixedRandomName("app")
		userName, _ = helpers.GetCredentials()
	})

	When("the app exists", func() {
		BeforeEach(func() {
			helpers.WithHelloWorldApp(func(appDir string) {
				Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir},
					PushCommandName, appName,
				)).Should(Exit(0))
			})
		})

		It("pushes the app and creates a new deployment", func() {
			helpers.WithHelloWorldApp(func(appDir string) {
				session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir},
					PushCommandName, appName, "--strategy", "rolling",
				)

				Eventually(session).Should(Say(`Updating app %s\.\.\.`, appName))
				Eventually(session).Should(Say(`Pushing app %s to org %s / space %s as %s\.\.\.`, appName, organization, space, userName))
				Eventually(session).Should(Say(`Getting app info\.\.\.`))
				Eventually(session).Should(Say(`Packaging files to upload\.\.\.`))
				Eventually(session).Should(Say(`Uploading files\.\.\.`))
				Eventually(session).Should(Say(`100.00%`))
				Eventually(session).Should(Say(`Waiting for API to complete processing files\.\.\.`))
				Eventually(session).Should(Say(`Staging app and tracing logs\.\.\.`))
				Eventually(session).Should(Say(`Starting deployment for app %s\.\.\.`, appName))
				Eventually(session).Should(Say(`Waiting for app to deploy\.\.\.`))
				Eventually(session).Should(Say(`name:\s+%s`, appName))
				Eventually(session).Should(Say(`requested state:\s+started`))
				Eventually(session).Should(Say(`routes:\s+%s.%s`, appName, helpers.DefaultSharedDomain()))
				Eventually(session).Should(Say(`type:\s+web`))
				Eventually(session).Should(Say(`start command:\s+%s`, helpers.StaticfileBuildpackStartCommand))
				Eventually(session).Should(Say(`#0\s+running`))
				Eventually(session).Should(Exit(0))
			})
		})
	})

	When("the app crashes", func() {
		BeforeEach(func() {
			helpers.WithHelloWorldApp(func(appDir string) {
				Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir},
					PushCommandName, appName,
				)).Should(Exit(0))
			})
		})

		It("times out", func() {
			helpers.WithCrashingApp(func(appDir string) {
				session := helpers.CustomCF(helpers.CFEnv{
					WorkingDirectory: appDir,
					EnvVars: map[string]string{"CF_STARTUP_TIMEOUT": "0.1"},
				}, PushCommandName, appName, "--strategy", "rolling")
				Eventually(session).Should(Say(`Updating app %s\.\.\.`, appName))
				Eventually(session).Should(Say(`Pushing app %s to org %s / space %s as %s\.\.\.`, appName, organization, space, userName))
				Eventually(session).Should(Say(`Getting app info\.\.\.`))
				Eventually(session).Should(Say(`Packaging files to upload\.\.\.`))
				Eventually(session).Should(Say(`Uploading files\.\.\.`))
				Eventually(session).Should(Say(`100.00%`))
				Eventually(session).Should(Say(`Waiting for API to complete processing files\.\.\.`))
				Eventually(session).Should(Say(`Staging app and tracing logs\.\.\.`))
				Eventually(session).Should(Say(`Starting deployment for app %s\.\.\.`, appName))
				Eventually(session).Should(Say(`Waiting for app to deploy\.\.\.`))
				Eventually(session).Should(Say(`FAILED`))
				Eventually(session.Err).Should(Say(`Start app timeout`))
				Eventually(session.Err).Should(Say(`TIP: Application must be listening on the right port\. Instead of hard coding the port, use the \$PORT environment variable\.`))
				Eventually(session.Err).Should(Say(`Use 'cf logs %s --recent' for more information`, appName))
				Eventually(session).Should(Exit(1))
			})
		})
	})
})
<commit_msg>Fix go fmt errors<commit_after>package push

import (
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("push with --strategy rolling", func() {
	var (
		appName  string
		userName string
	)

	BeforeEach(func() {
		appName = helpers.PrefixedRandomName("app")
		userName, _ = helpers.GetCredentials()
	})

	When("the app exists", func() {
		BeforeEach(func() {
			helpers.WithHelloWorldApp(func(appDir string) {
				Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir},
					PushCommandName, appName,
				)).Should(Exit(0))
			})
		})

		It("pushes the app and creates a new deployment", func() {
			helpers.WithHelloWorldApp(func(appDir string) {
				session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir},
					PushCommandName, appName, "--strategy", "rolling",
				)

				Eventually(session).Should(Say(`Updating app %s\.\.\.`, appName))
				Eventually(session).Should(Say(`Pushing app %s to org %s / space %s as %s\.\.\.`, appName, organization, space, userName))
				Eventually(session).Should(Say(`Getting app info\.\.\.`))
				Eventually(session).Should(Say(`Packaging files to upload\.\.\.`))
				Eventually(session).Should(Say(`Uploading files\.\.\.`))
				Eventually(session).Should(Say(`100.00%`))
				Eventually(session).Should(Say(`Waiting for API to complete processing files\.\.\.`))
				Eventually(session).Should(Say(`Staging app and tracing logs\.\.\.`))
				Eventually(session).Should(Say(`Starting deployment for app %s\.\.\.`, appName))
				Eventually(session).Should(Say(`Waiting for app to deploy\.\.\.`))
				Eventually(session).Should(Say(`name:\s+%s`, appName))
				Eventually(session).Should(Say(`requested state:\s+started`))
				Eventually(session).Should(Say(`routes:\s+%s.%s`, appName, helpers.DefaultSharedDomain()))
				Eventually(session).Should(Say(`type:\s+web`))
				Eventually(session).Should(Say(`start command:\s+%s`, helpers.StaticfileBuildpackStartCommand))
				Eventually(session).Should(Say(`#0\s+running`))
				Eventually(session).Should(Exit(0))
			})
		})
	})

	When("the app crashes", func() {
		BeforeEach(func() {
			helpers.WithHelloWorldApp(func(appDir string) {
				Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: appDir},
					PushCommandName, appName,
				)).Should(Exit(0))
			})
		})

		It("times out", func() {
			helpers.WithCrashingApp(func(appDir string) {
				session := helpers.CustomCF(helpers.CFEnv{
					WorkingDirectory: appDir,
					EnvVars:          map[string]string{"CF_STARTUP_TIMEOUT": "0.1"},
				}, PushCommandName, appName, "--strategy", "rolling")
				Eventually(session).Should(Say(`Updating app %s\.\.\.`, appName))
				Eventually(session).Should(Say(`Pushing app %s to org %s / space %s as %s\.\.\.`, appName, organization, space, userName))
				Eventually(session).Should(Say(`Getting app info\.\.\.`))
				Eventually(session).Should(Say(`Packaging files to upload\.\.\.`))
				Eventually(session).Should(Say(`Uploading files\.\.\.`))
				Eventually(session).Should(Say(`100.00%`))
				Eventually(session).Should(Say(`Waiting for API to complete processing files\.\.\.`))
				Eventually(session).Should(Say(`Staging app and tracing logs\.\.\.`))
				Eventually(session).Should(Say(`Starting deployment for app %s\.\.\.`, appName))
				Eventually(session).Should(Say(`Waiting for app to deploy\.\.\.`))
				Eventually(session).Should(Say(`FAILED`))
				Eventually(session.Err).Should(Say(`Start app timeout`))
				Eventually(session.Err).Should(Say(`TIP: Application must be listening on the right port\. Instead of hard coding the port, use the \$PORT environment variable\.`))
				Eventually(session.Err).Should(Say(`Use 'cf logs %s --recent' for more information`, appName))
				Eventually(session).Should(Exit(1))
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2020 The Ebiten 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 opengl

import (
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/shaderir"
)

type Shader struct {
	id       driver.ShaderID
	graphics *Graphics

	ir *shaderir.Program
	p  program
}

func NewShader(id driver.ShaderID, graphics *Graphics, program *shaderir.Program) (*Shader, error) {
	s := &Shader{
		id:       id,
		graphics: graphics,
		ir:       program,
	}
	if err := s.compile(); err != nil {
		return nil, err
	}
	return s, nil
}

func (s *Shader) ID() driver.ShaderID {
	return s.id
}

func (s *Shader) Dispose() {
	s.graphics.context.deleteProgram(s.p)
	s.graphics.removeShader(s)
}

func (s *Shader) compile() error {
	vssrc, fssrc := s.ir.Glsl()
	println(vssrc, fssrc)

	vs, err := s.graphics.context.newShader(vertexShader, vssrc)
	if err != nil {
		return err
	}
	defer s.graphics.context.deleteShader(vs)

	fs, err := s.graphics.context.newShader(fragmentShader, fssrc)
	if err != nil {
		return err
	}
	defer s.graphics.context.deleteShader(vs)

	p, err := s.graphics.context.newProgram([]shader{vs, fs}, theArrayBufferLayout.names())
	if err != nil {
		return err
	}

	s.p = p
	return nil
}
<commit_msg>graphicsdriver/opengl: Bug fix: deleted a wrong shader program<commit_after>// Copyright 2020 The Ebiten 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 opengl

import (
	"github.com/hajimehoshi/ebiten/internal/driver"
	"github.com/hajimehoshi/ebiten/internal/shaderir"
)

type Shader struct {
	id       driver.ShaderID
	graphics *Graphics

	ir *shaderir.Program
	p  program
}

func NewShader(id driver.ShaderID, graphics *Graphics, program *shaderir.Program) (*Shader, error) {
	s := &Shader{
		id:       id,
		graphics: graphics,
		ir:       program,
	}
	if err := s.compile(); err != nil {
		return nil, err
	}
	return s, nil
}

func (s *Shader) ID() driver.ShaderID {
	return s.id
}

func (s *Shader) Dispose() {
	s.graphics.context.deleteProgram(s.p)
	s.graphics.removeShader(s)
}

func (s *Shader) compile() error {
	vssrc, fssrc := s.ir.Glsl()
	println(vssrc, fssrc)

	vs, err := s.graphics.context.newShader(vertexShader, vssrc)
	if err != nil {
		return err
	}
	defer s.graphics.context.deleteShader(vs)

	fs, err := s.graphics.context.newShader(fragmentShader, fssrc)
	if err != nil {
		return err
	}
	defer s.graphics.context.deleteShader(fs)

	p, err := s.graphics.context.newProgram([]shader{vs, fs}, theArrayBufferLayout.names())
	if err != nil {
		return err
	}

	s.p = p
	return nil
}
<|endoftext|>


<commit_before>package main

import(
  "fmt"
  "os"
  "io/ioutil"
  "path/filepath"
  "os/exec"
  "strings"
)

const(
  LUNCHY_VERSION = "0.1.0"
)

func printUsage() {
  fmt.Printf("Lunchy %s, the friendly launchctl wrapper\n", LUNCHY_VERSION)
  fmt.Println("Usage: lunchy [start|stop|restart|list|status|install|show|edit] [options]")
}

func findPlists(path string) []string {
  result := []string{}
  files, err := ioutil.ReadDir(path)

  if err != nil {
    return result
  }

  for _, file := range files {
    if !file.IsDir() {
      if (filepath.Ext(file.Name())) == ".plist" {
        name := strings.Replace(file.Name(), ".plist", "", -1)
        result = append(result, name)
      }
    }
  }

  return result
}

func getPlists() []string {
  path := fmt.Sprintf("%s/Library/LaunchAgents", os.Getenv("HOME"))
  files := findPlists(path)

  return files
}

func sliceIncludes(slice []string, match string) bool {
  for _, val := range slice {
    if val == match {
      return true
    }
  }

  return false
}

func printList() {
  for _, file := range getPlists() {
    fmt.Println(file)
  }
}

func printStatus(args []string) {
  out, err := exec.Command("launchctl", "list").Output()

  if err != nil {
    fmt.Println("Failed to execute", err)
    os.Exit(1)
  }

  installed := getPlists()
  lines := strings.Split(strings.TrimSpace(string(out)), "\n")

  for _, line := range lines {
    chunks := strings.Split(line, "\t")

    if sliceIncludes(installed, chunks[2]) {
      fmt.Println(line)
    }
  }
}

func main() {
  args := os.Args

  if (len(args) == 1) {
    printUsage()
    os.Exit(1)
  }

  switch args[1] {
  default:
    printUsage()
    os.Exit(1)
  case "list":
    printList()
    return
  case "status":
    printStatus(args)
    return
  }
}<commit_msg>Add pattern matching in status<commit_after>package main

import(
  "fmt"
  "os"
  "io/ioutil"
  "path/filepath"
  "os/exec"
  "strings"
)

const(
  LUNCHY_VERSION = "0.1.0"
)

func printUsage() {
  fmt.Printf("Lunchy %s, the friendly launchctl wrapper\n", LUNCHY_VERSION)
  fmt.Println("Usage: lunchy [start|stop|restart|list|status|install|show|edit] [options]")
}

func findPlists(path string) []string {
  result := []string{}
  files, err := ioutil.ReadDir(path)

  if err != nil {
    return result
  }

  for _, file := range files {
    if !file.IsDir() {
      if (filepath.Ext(file.Name())) == ".plist" {
        name := strings.Replace(file.Name(), ".plist", "", -1)
        result = append(result, name)
      }
    }
  }

  return result
}

func getPlists() []string {
  path := fmt.Sprintf("%s/Library/LaunchAgents", os.Getenv("HOME"))
  files := findPlists(path)

  return files
}

func sliceIncludes(slice []string, match string) bool {
  for _, val := range slice {
    if val == match {
      return true
    }
  }

  return false
}

func printList() {
  for _, file := range getPlists() {
    fmt.Println(file)
  }
}

func printStatus(args []string) {
  out, err := exec.Command("launchctl", "list").Output()

  if err != nil {
    fmt.Println("Failed to execute", err)
    os.Exit(1)
  }

  pattern := ""

  if len(args) == 3 {
    pattern = args[2]
  }

  installed := getPlists()
  lines := strings.Split(strings.TrimSpace(string(out)), "\n")

  for _, line := range lines {
    chunks := strings.Split(line, "\t")

    if len(pattern) > 0 {
      if strings.Index(chunks[2], pattern) != -1 {
        if sliceIncludes(installed, chunks[2]) {
          fmt.Println(line)
        }
      }
    } else {
      if sliceIncludes(installed, chunks[2]) {
        fmt.Println(line)
      }
    }
  }
}

func main() {
  args := os.Args

  if (len(args) == 1) {
    printUsage()
    os.Exit(1)
  }

  switch args[1] {
  default:
    printUsage()
    os.Exit(1)
  case "list":
    printList()
    return
  case "status":
    printStatus(args)
    return
  }
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

const (
	LUNCHY_VERSION = "0.1.5"
)

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func fileCopy(src string, dst string) error {
	s, err := os.Open(src)
	if err != nil {
		return err
	}

	defer s.Close()

	d, err := os.Create(dst)
	if err != nil {
		return err
	}

	if _, err := io.Copy(d, s); err != nil {
		d.Close()
		return err
	}

	return d.Close()
}

func findPlists(path string) []string {
	result := []string{}
	files, err := ioutil.ReadDir(path)

	if err != nil {
		return result
	}

	for _, file := range files {
		if !file.IsDir() {
			if (filepath.Ext(file.Name())) == ".plist" {
				name := strings.Replace(file.Name(), ".plist", "", -1)
				result = append(result, name)
			}
		}
	}

	return result
}

func getPlists() []string {
	path := fmt.Sprintf("%s/Library/LaunchAgents", os.Getenv("HOME"))
	files := findPlists(path)

	return files
}

func getPlist(name string) string {
	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			return plist
		}
	}

	return ""
}

func sliceIncludes(slice []string, match string) bool {
	for _, val := range slice {
		if val == match {
			return true
		}
	}

	return false
}

func printUsage() {
	fmt.Printf("Lunchy %s, the friendly launchctl wrapper\n", LUNCHY_VERSION)
	fmt.Println("Usage: lunchy [start|stop|restart|list|status|install|show|edit|remove|scan] [options]")
}

func printList() {
	for _, file := range getPlists() {
		fmt.Println(file)
	}
}

func printStatus(args []string) {
	out, err := exec.Command("launchctl", "list").Output()

	if err != nil {
		fatal("failed to get process list")
	}

	pattern := ""

	if len(args) == 3 {
		pattern = args[2]
	}

	installed := getPlists()
	lines := strings.Split(strings.TrimSpace(string(out)), "\n")

	for _, line := range lines {
		chunks := strings.Split(line, "\t")
		clean_line := strings.Replace(line, "\t", " ", -1)

		if len(pattern) > 0 {
			if strings.Index(chunks[2], pattern) != -1 {
				if sliceIncludes(installed, chunks[2]) {
					fmt.Println(clean_line)
				}
			}
		} else {
			if sliceIncludes(installed, chunks[2]) {
				fmt.Println(clean_line)
			}
		}
	}
}

func exitWithInvalidArgs(args []string, msg string) {
	if len(args) < 3 {
		fmt.Println(msg)
		os.Exit(1)
	}
}

func startDaemons(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			startDaemon(plist)
		}
	}
}

func startDaemon(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	_, err := exec.Command("launchctl", "load", path).Output()

	if err != nil {
		fmt.Println("failed to start", name)
		return
	}

	fmt.Println("started", name)
}

func stopDaemons(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			stopDaemon(plist)
		}
	}
}

func stopDaemon(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	_, err := exec.Command("launchctl", "unload", path).Output()

	if err != nil {
		fmt.Println("failed to stop", name)
		return
	}

	fmt.Println("stopped", name)
}

func restartDaemons(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			stopDaemon(plist)
			startDaemon(plist)
		}
	}
}

func showPlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			printPlistContent(plist)
			return
		}
	}
}

func printPlistContent(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	contents, err := ioutil.ReadFile(path)

	if err != nil {
		fatal("unable to read plist")
	}

	fmt.Printf(string(contents))
}

func editPlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			editPlistContent(plist)
			return
		}
	}
}

func editPlistContent(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	editor := os.Getenv("EDITOR")

	if len(editor) == 0 {
		fatal("EDITOR environment variable is not set")
	}

	cmd := exec.Command(editor, path)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout

	cmd.Start()
	cmd.Wait()
}

func installPlist(args []string) {
	exitWithInvalidArgs(args, "path required")

	path := args[2]

	if !fileExists(path) {
		fatal("source file does not exist")
	}

	info, _ := os.Stat(path)
	base_path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")
	new_path := fmt.Sprintf("%s/%s", base_path, info.Name())

	if fileExists(new_path) && os.Remove(new_path) != nil {
		fatal("unable to delete existing plist")
	}

	if fileCopy(path, new_path) != nil {
		fatal("failed to copy file")
	}

	fmt.Println(path, "installed to", base_path)
}

func removePlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]
	base_path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			path := fmt.Sprintf("%s/%s.plist", base_path, plist)

			if os.Remove(path) == nil {
				fmt.Println("removed", path)
			} else {
				fmt.Println("failed to remove", path)
			}
		}
	}
}

func scanPath(args []string) {
	path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")

	if len(args) >= 3 {
		path = args[2]
	}

	for _, f := range findPlists(path) {
		fmt.Println(f)
	}
}

func fatal(message string) {
	fmt.Println(message)
	os.Exit(1)
}

func main() {
	args := os.Args

	if len(args) == 1 {
		printUsage()
		os.Exit(1)
	}

	switch args[1] {
	default:
		printUsage()
		os.Exit(1)
	case "help":
		printUsage()
		return
	case "list", "ls":
		printList()
		return
	case "status", "ps":
		printStatus(args)
		return
	case "start":
		startDaemons(args)
		return
	case "stop":
		stopDaemons(args)
		return
	case "restart":
		restartDaemons(args)
		return
	case "show":
		showPlist(args)
		return
	case "edit":
		editPlist(args)
		return
	case "install", "add":
		installPlist(args)
		return
	case "remove", "rm":
		removePlist(args)
		return
	case "scan":
		scanPath(args)
		return
	}
}
<commit_msg>Allow scanning all homebrew-based plists with 'scan homebrew' command<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

const (
	LUNCHY_VERSION = "0.1.5"
)

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func fileCopy(src string, dst string) error {
	s, err := os.Open(src)
	if err != nil {
		return err
	}

	defer s.Close()

	d, err := os.Create(dst)
	if err != nil {
		return err
	}

	if _, err := io.Copy(d, s); err != nil {
		d.Close()
		return err
	}

	return d.Close()
}

func findPlists(path string) []string {
	output, err := exec.Command("find", path, "-name", "homebrew.*.plist", "-type", "f").Output()
	if err != nil {
		return []string{}
	}

	return strings.Split(strings.TrimSpace(string(output)), "\n")
}

func getPlists() []string {
	path := fmt.Sprintf("%s/Library/LaunchAgents", os.Getenv("HOME"))
	files := findPlists(path)

	return files
}

func getPlist(name string) string {
	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			return plist
		}
	}

	return ""
}

func sliceIncludes(slice []string, match string) bool {
	for _, val := range slice {
		if val == match {
			return true
		}
	}

	return false
}

func printUsage() {
	fmt.Printf("Lunchy %s, the friendly launchctl wrapper\n", LUNCHY_VERSION)
	fmt.Println("Usage: lunchy [start|stop|restart|list|status|install|show|edit|remove|scan] [options]")
}

func printList() {
	for _, file := range getPlists() {
		fmt.Println(file)
	}
}

func printStatus(args []string) {
	out, err := exec.Command("launchctl", "list").Output()

	if err != nil {
		fatal("failed to get process list")
	}

	pattern := ""

	if len(args) == 3 {
		pattern = args[2]
	}

	installed := getPlists()
	lines := strings.Split(strings.TrimSpace(string(out)), "\n")

	for _, line := range lines {
		chunks := strings.Split(line, "\t")
		clean_line := strings.Replace(line, "\t", " ", -1)

		if len(pattern) > 0 {
			if strings.Index(chunks[2], pattern) != -1 {
				if sliceIncludes(installed, chunks[2]) {
					fmt.Println(clean_line)
				}
			}
		} else {
			if sliceIncludes(installed, chunks[2]) {
				fmt.Println(clean_line)
			}
		}
	}
}

func exitWithInvalidArgs(args []string, msg string) {
	if len(args) < 3 {
		fmt.Println(msg)
		os.Exit(1)
	}
}

func startDaemons(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			startDaemon(plist)
		}
	}
}

func startDaemon(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	_, err := exec.Command("launchctl", "load", path).Output()

	if err != nil {
		fmt.Println("failed to start", name)
		return
	}

	fmt.Println("started", name)
}

func stopDaemons(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			stopDaemon(plist)
		}
	}
}

func stopDaemon(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	_, err := exec.Command("launchctl", "unload", path).Output()

	if err != nil {
		fmt.Println("failed to stop", name)
		return
	}

	fmt.Println("stopped", name)
}

func restartDaemons(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			stopDaemon(plist)
			startDaemon(plist)
		}
	}
}

func showPlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			printPlistContent(plist)
			return
		}
	}
}

func printPlistContent(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	contents, err := ioutil.ReadFile(path)

	if err != nil {
		fatal("unable to read plist")
	}

	fmt.Printf(string(contents))
}

func editPlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			editPlistContent(plist)
			return
		}
	}
}

func editPlistContent(name string) {
	path := fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", os.Getenv("HOME"), name)
	editor := os.Getenv("EDITOR")

	if len(editor) == 0 {
		fatal("EDITOR environment variable is not set")
	}

	cmd := exec.Command(editor, path)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout

	cmd.Start()
	cmd.Wait()
}

func installPlist(args []string) {
	exitWithInvalidArgs(args, "path required")

	path := args[2]

	if !fileExists(path) {
		fatal("source file does not exist")
	}

	info, _ := os.Stat(path)
	base_path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")
	new_path := fmt.Sprintf("%s/%s", base_path, info.Name())

	if fileExists(new_path) && os.Remove(new_path) != nil {
		fatal("unable to delete existing plist")
	}

	if fileCopy(path, new_path) != nil {
		fatal("failed to copy file")
	}

	fmt.Println(path, "installed to", base_path)
}

func removePlist(args []string) {
	exitWithInvalidArgs(args, "name required")

	name := args[2]
	base_path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")

	for _, plist := range getPlists() {
		if strings.Index(plist, name) != -1 {
			path := fmt.Sprintf("%s/%s.plist", base_path, plist)

			if os.Remove(path) == nil {
				fmt.Println("removed", path)
			} else {
				fmt.Println("failed to remove", path)
			}
		}
	}
}

func scanPath(args []string) {
	path := fmt.Sprintf("%s/%s", os.Getenv("HOME"), "Library/LaunchAgents")

	if len(args) >= 3 {
		path = args[2]
	}

	// This is a handy override to find all homebrew-based lists
	if path == "homebrew" {
		path = "/usr/local/Cellar"
	}

	for _, f := range findPlists(path) {
		fmt.Println(f)
	}
}

func fatal(message string) {
	fmt.Println(message)
	os.Exit(1)
}

func main() {
	args := os.Args

	if len(args) == 1 {
		printUsage()
		os.Exit(1)
	}

	switch args[1] {
	default:
		printUsage()
		os.Exit(1)
	case "help":
		printUsage()
		return
	case "list", "ls":
		printList()
		return
	case "status", "ps":
		printStatus(args)
		return
	case "start":
		startDaemons(args)
		return
	case "stop":
		stopDaemons(args)
		return
	case "restart":
		restartDaemons(args)
		return
	case "show":
		showPlist(args)
		return
	case "edit":
		editPlist(args)
		return
	case "install", "add":
		installPlist(args)
		return
	case "remove", "rm":
		removePlist(args)
		return
	case "scan":
		scanPath(args)
		return
	}
}
<|endoftext|>


<commit_before>package hu

func (interpreter *Interpreter) quote(object Object, environment *Environment) Object {
	return cons(quote_symbol, object)
}

func (interpreter *Interpreter) unquote(object Object, environment *Environment) Object {
	// TODO check that car is quote
	return cdr(object)
}

func (interpreter *Interpreter) evalList(list Object, environment *Environment) Object {
	eval := func(object Object) Object { return interpreter.evaluate(object, environment) }
	return list_from(list, eval)
}

func (interpreter *Interpreter) define(object Object, environment *Environment) Object {
	var variable, value Object

	if is_symbol(car(object)) {
		variable = car(object)
		value = car(cdr(object))
	} else {
		variable = car(car(object))
		parameters := cdr(car(object))
		body := cdr(object)
		value = interpreter.lambda(cons(parameters, body), environment)
	}

	environment.Define(variable, value)
	return nil
}

func (interpreter *Interpreter) set(object Object, environment *Environment) Object {
	variable := car(object)
	value := car(cdr(object))
	value = interpreter.evaluate(value, environment)
	environment.Set(variable, value)
	return nil
}

func (interpreter *Interpreter) lambda(object Object, outer *Environment) Object {
	parameters := car(object)
	function := cdr(object)
	f := func(interpreter *Interpreter, object Object, environment *Environment) Object {
		operands := interpreter.evalList(object, environment)
		environment = outer.Extend(parameters, operands)
		function := interpreter.evalList(function, environment)
		return interpreter.begin(function, environment)
	}
	return &PrimitiveFunctionObject{f}
}

func (interpreter *Interpreter) begin(object Object, environment *Environment) Object {
	var result Object
	for expressions := object; expressions != nil; expressions = cdr(expressions) {
		expression := car(expressions)
		result = interpreter.evaluate(expression, environment)
	}
	return result
}

func (interpreter *Interpreter) and(object Object, environment *Environment) Object {
	result := TRUE
	tests := object
	for exp := tests; exp != nil; exp = cdr(exp) {
		first_exp := car(exp)
		result = interpreter.evaluate(first_exp, environment)
		if is_false(result) {
			return result
		}
	}
	return result
}

func (interpreter *Interpreter) or(object Object, environment *Environment) Object {
	result := FALSE
	tests := object
	for exp := tests; exp != nil; exp = cdr(exp) {
		first_exp := car(exp)
		result = interpreter.evaluate(first_exp, environment)
		if is_true(result) {
			return result
		}
	}
	return result
}

func (interpreter *Interpreter) ifPrimitive(object Object, environment *Environment) Object {
	if_predicate := car(object)
	if is_true(interpreter.evaluate(if_predicate, environment)) {
		if_consequent := car(cdr(object))
		object = if_consequent
	} else {
		var if_alternative Object
		if is_the_empty_list(cdr(cdr(object))) {
			if_alternative = FALSE
		} else {
			if_alternative = car(cdr(cdr(object)))
		}
		object = if_alternative
	}
	return interpreter.evaluate(object, environment)
}

func (interpreter *Interpreter) apply(object Object, environment *Environment) Object {
	operator := car(object)
	operands := cdr(object)
	return &ExpressionObject{operator, operands}
}

func (interpreter *Interpreter) evalPrimitive(object Object, environment *Environment) Object {
	expression := car(object)
	environment = car(cdr(object)).(*Environment)
	return interpreter.evaluate(expression, environment)
}

func (interpreter *Interpreter) let(object Object, environment *Environment) Object {
	bindings := car(object)
	body := cdr(object)

	binding_parameter := func(binding Object) Object { return car(binding) }
	parameters := list_from(bindings, binding_parameter)

	binding_arguments := func(binding Object) Object { return car(cdr(binding)) }
	arguments := list_from(bindings, binding_arguments)

	operator := interpreter.lambda(cons(parameters, body), environment)
	operands := arguments

	return &ExpressionObject{operator, operands}
}
<commit_msg>Evaluate value expression before binding it. <commit_after>package hu

func (interpreter *Interpreter) quote(object Object, environment *Environment) Object {
	return cons(quote_symbol, object)
}

func (interpreter *Interpreter) unquote(object Object, environment *Environment) Object {
	// TODO check that car is quote
	return cdr(object)
}

func (interpreter *Interpreter) evalList(list Object, environment *Environment) Object {
	eval := func(object Object) Object { return interpreter.evaluate(object, environment) }
	return list_from(list, eval)
}

func (interpreter *Interpreter) define(object Object, environment *Environment) Object {
	var variable, value Object

	if is_symbol(car(object)) {
		variable = car(object)
		value = interpreter.evaluate(car(cdr(object)), environment)
	} else {
		variable = car(car(object))
		parameters := cdr(car(object))
		body := cdr(object)
		value = interpreter.lambda(cons(parameters, body), environment)
	}

	environment.Define(variable, value)
	return nil
}

func (interpreter *Interpreter) set(object Object, environment *Environment) Object {
	variable := car(object)
	value := car(cdr(object))
	value = interpreter.evaluate(value, environment)
	environment.Set(variable, value)
	return nil
}

func (interpreter *Interpreter) lambda(object Object, outer *Environment) Object {
	parameters := car(object)
	function := cdr(object)
	f := func(interpreter *Interpreter, object Object, environment *Environment) Object {
		operands := interpreter.evalList(object, environment)
		environment = outer.Extend(parameters, operands)
		function := interpreter.evalList(function, environment)
		return interpreter.begin(function, environment)
	}
	return &PrimitiveFunctionObject{f}
}

func (interpreter *Interpreter) begin(object Object, environment *Environment) Object {
	var result Object
	for expressions := object; expressions != nil; expressions = cdr(expressions) {
		expression := car(expressions)
		result = interpreter.evaluate(expression, environment)
	}
	return result
}

func (interpreter *Interpreter) and(object Object, environment *Environment) Object {
	result := TRUE
	tests := object
	for exp := tests; exp != nil; exp = cdr(exp) {
		first_exp := car(exp)
		result = interpreter.evaluate(first_exp, environment)
		if is_false(result) {
			return result
		}
	}
	return result
}

func (interpreter *Interpreter) or(object Object, environment *Environment) Object {
	result := FALSE
	tests := object
	for exp := tests; exp != nil; exp = cdr(exp) {
		first_exp := car(exp)
		result = interpreter.evaluate(first_exp, environment)
		if is_true(result) {
			return result
		}
	}
	return result
}

func (interpreter *Interpreter) ifPrimitive(object Object, environment *Environment) Object {
	if_predicate := car(object)
	if is_true(interpreter.evaluate(if_predicate, environment)) {
		if_consequent := car(cdr(object))
		object = if_consequent
	} else {
		var if_alternative Object
		if is_the_empty_list(cdr(cdr(object))) {
			if_alternative = FALSE
		} else {
			if_alternative = car(cdr(cdr(object)))
		}
		object = if_alternative
	}
	return interpreter.evaluate(object, environment)
}

func (interpreter *Interpreter) apply(object Object, environment *Environment) Object {
	operator := car(object)
	operands := cdr(object)
	return &ExpressionObject{operator, operands}
}

func (interpreter *Interpreter) evalPrimitive(object Object, environment *Environment) Object {
	expression := car(object)
	environment = car(cdr(object)).(*Environment)
	return interpreter.evaluate(expression, environment)
}

func (interpreter *Interpreter) let(object Object, environment *Environment) Object {
	bindings := car(object)
	body := cdr(object)

	binding_parameter := func(binding Object) Object { return car(binding) }
	parameters := list_from(bindings, binding_parameter)

	binding_arguments := func(binding Object) Object { return car(cdr(binding)) }
	arguments := list_from(bindings, binding_arguments)

	operator := interpreter.lambda(cons(parameters, body), environment)
	operands := arguments

	return &ExpressionObject{operator, operands}
}
<|endoftext|>


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

package main

import (
	"encoding/gob"
	"flag"
	"fmt"
	"os"
)

type dumpTraceFlags struct {
	TracePath string
}

var (
	dumpTraceFlagset = flag.NewFlagSet("dump-trace", flag.ExitOnError)
	_dumpTraceFlags  = dumpTraceFlags{}
)

func init() {
	dumpTraceFlagset.StringVar(&_dumpTraceFlags.TracePath, "trace-path", "", "path of trace data file")
}

func dumpTrace(args []string) {
	dumpTraceFlagset.Parse(args)

	if _dumpTraceFlags.TracePath == "" {
		fmt.Printf("specify path of trace data file\n")
		os.Exit(1)
	}

	file, err := os.Open(_dumpTraceFlags.TracePath)
	if err != nil {
		fmt.Printf("failed to open trace data file(%s): %s\n", _dumpTraceFlags.TracePath, err)
		os.Exit(1)
	}

	dec := gob.NewDecoder(file)
	var trace SingleTrace
	derr := dec.Decode(&trace)
	if derr != nil {
		fmt.Printf("failed to decode trace file(%s): %s\n", _dumpTraceFlags.TracePath, err)
		os.Exit(1)
	}

	for i, ev := range trace.EventSequence {
		fmt.Printf("%d: %s, %s(%s)\n", i, ev.ProcId, ev.EventType, ev.EventParam)
	}
}

func runSearchTools(name string, args []string) {
	switch name {
	case "dump-trace":
		dumpTrace(args)
	default:
		fmt.Printf("unknown subcommand: %s\n", name)
		os.Exit(1)
	}
}
<commit_msg>search tools: a stub for duplication calculator<commit_after>// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/gob"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
)

type dumpTraceFlags struct {
	TracePath string
}

type calcDuplicationFlags struct {
	TraceDir string
}

var (
	dumpTraceFlagset = flag.NewFlagSet("dump-trace", flag.ExitOnError)
	_dumpTraceFlags  = dumpTraceFlags{}

	calcDuplicationFlagset = flag.NewFlagSet("calc-duplication", flag.ExitOnError)
	_calcDuplicationFlags  = calcDuplicationFlags{}
)

func init() {
	dumpTraceFlagset.StringVar(&_dumpTraceFlags.TracePath, "trace-path", "", "path of trace data file")

	calcDuplicationFlagset.StringVar(&_calcDuplicationFlags.TraceDir, "trace-dir", "", "path of trace data directory")
}

func dumpTrace(args []string) {
	dumpTraceFlagset.Parse(args)

	if _dumpTraceFlags.TracePath == "" {
		fmt.Printf("specify path of trace data file\n")
		os.Exit(1)
	}

	file, err := os.Open(_dumpTraceFlags.TracePath)
	if err != nil {
		fmt.Printf("failed to open trace data file(%s): %s\n", _dumpTraceFlags.TracePath, err)
		os.Exit(1)
	}

	dec := gob.NewDecoder(file)
	var trace SingleTrace
	derr := dec.Decode(&trace)
	if derr != nil {
		fmt.Printf("failed to decode trace file(%s): %s\n", _dumpTraceFlags.TracePath, err)
		os.Exit(1)
	}

	for i, ev := range trace.EventSequence {
		fmt.Printf("%d: %s, %s(%s)\n", i, ev.ProcId, ev.EventType, ev.EventParam)
	}
}

func calcDuplication(args []string) {
	calcDuplicationFlagset.Parse(args)

	traceDir := _calcDuplicationFlags.TraceDir
	if traceDir == "" {
		fmt.Printf("specify directory path of trace data\n")
		os.Exit(1)
	}

	des, err := ioutil.ReadDir(traceDir)
	if err != nil {
		fmt.Printf("failed to read directory(%s): %s\n", traceDir, err)
		os.Exit(1)
	}

	if len(des) == 0 {
		fmt.Printf("directory %s is empty\n", traceDir)
		os.Exit(1)
	}

	infoFile, oerr := os.Open(traceDir + "/" + SearchModeInfoPath)
	if oerr != nil {
		fmt.Printf("failed to read info file of search mode: %s\n", oerr)
		os.Exit(1)
	}

	infoDec := gob.NewDecoder(infoFile)
	var info SearchModeInfo
	derr := infoDec.Decode(&info)
	if derr != nil {
		fmt.Printf("failed to decode info file: %s\n", derr)
		os.Exit(1)
	}

	fmt.Printf("a number of collected traces: %d\n", info.NrCollectedTraces)
}

func runSearchTools(name string, args []string) {
	switch name {
	case "dump-trace":
		dumpTrace(args)
	case "calc-duplication":
		calcDuplication(args)
	default:
		fmt.Printf("unknown subcommand: %s\n", name)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package notmain

import (
	"bufio"
	"compress/gzip"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"os"
	"regexp"
	"sort"
	"strings"
	"time"

	"github.com/letsencrypt/boulder/cmd"
)

var raIssuanceLineRE = regexp.MustCompile(`Certificate request - successful JSON=(.*)`)

// TODO: Extract the "Valid for issuance: (true|false)" field too.
var vaCAALineRE = regexp.MustCompile(`Checked CAA records for ([a-z0-9-.*]+), \[Present: (true|false)`)

type issuanceEvent struct {
	SerialNumber string
	Names        []string
	Requester    int64

	issuanceTime time.Time
}

func openFile(path string) (*bufio.Scanner, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	var reader io.Reader
	reader = f
	if strings.HasSuffix(path, ".gz") {
		reader, err = gzip.NewReader(f)
		if err != nil {
			return nil, err
		}
	}
	scanner := bufio.NewScanner(reader)
	return scanner, nil
}

func parseTimestamp(line []byte) (time.Time, error) {
	datestamp, err := time.Parse(time.RFC3339, string(line[0:32]))
	if err != nil {
		return time.Time{}, err
	}
	return datestamp, nil
}

// loadIssuanceLog processes a single issuance (RA) log file. It returns a map
// of names to slices of timestamps at which certificates for those names were
// issued. It also returns the earliest and latest timestamps seen, to allow
// CAA log processing to quickly skip irrelevant entries.
func loadIssuanceLog(path string) (map[string][]time.Time, time.Time, time.Time, error) {
	scanner, err := openFile(path)
	if err != nil {
		return nil, time.Time{}, time.Time{}, fmt.Errorf("failed to open %q: %w", path, err)
	}

	linesCount := 0
	earliest := time.Time{}
	latest := time.Time{}

	issuanceMap := map[string][]time.Time{}
	for scanner.Scan() {
		line := scanner.Bytes()
		linesCount++

		matches := raIssuanceLineRE.FindSubmatch(line)
		if matches == nil {
			continue
		}
		if len(matches) != 2 {
			return nil, earliest, latest, fmt.Errorf("line %d: unexpected number of regex matches", linesCount)
		}

		var ie issuanceEvent
		err := json.Unmarshal(matches[1], &ie)
		if err != nil {
			return nil, earliest, latest, fmt.Errorf("line %d: failed to unmarshal JSON: %w", linesCount, err)
		}

		// Populate the issuance time from the syslog timestamp, rather than the
		// ResponseTime member of the JSON. This makes testing a lot simpler because
		// of how we mess with time sometimes. Given that these timestamps are
		// generated on the same system, they should be tightly coupled anyway.
		ie.issuanceTime, err = parseTimestamp(line)
		if err != nil {
			return nil, earliest, latest, fmt.Errorf("line %d: failed to parse timestamp: %w", linesCount, err)
		}

		if earliest.IsZero() || ie.issuanceTime.Before(earliest) {
			earliest = ie.issuanceTime
		}
		if latest.IsZero() || ie.issuanceTime.After(latest) {
			latest = ie.issuanceTime
		}
		for _, name := range ie.Names {
			issuanceMap[name] = append(issuanceMap[name], ie.issuanceTime)
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, earliest, latest, err
	}

	return issuanceMap, earliest, latest, nil
}

// processCAALog processes a single CAA (VA) log file. It modifies the input map
// (of issuance names to times, as returned by `loadIssuanceLog`) to remove any
// timestamps which are covered by (i.e. less than 8 hours after) a CAA check
// for that name in the log file. It also prunes any names whose slice of
// issuance times becomes empty.
func processCAALog(path string, issuances map[string][]time.Time, earliest time.Time, latest time.Time, tolerance time.Duration) error {
	scanner, err := openFile(path)
	if err != nil {
		return fmt.Errorf("failed to open %q: %w", path, err)
	}

	linesCount := 0

	for scanner.Scan() {
		line := scanner.Bytes()
		linesCount++

		matches := vaCAALineRE.FindSubmatch(line)
		if matches == nil {
			continue
		}
		if len(matches) != 3 {
			return fmt.Errorf("line %d: unexpected number of regex matches", linesCount)
		}
		name := string(matches[1])
		present := string(matches[2])

		checkTime, err := parseTimestamp(line)
		if err != nil {
			return fmt.Errorf("line %d: failed to parse timestamp: %w", linesCount, err)
		}

		// Don't bother processing rows that definitely fall outside the period we
		// care about.
		if checkTime.After(latest) || checkTime.Before(earliest.Add(-8*time.Hour)) {
			continue
		}

		// TODO: Only remove covered issuance timestamps if the CAA check actually
		// said that we're allowed to issue (i.e. had "Valid for issuance: true").
		issuances[name] = removeCoveredTimestamps(issuances[name], checkTime, tolerance)
		if len(issuances[name]) == 0 {
			delete(issuances, name)
		}

		// If the CAA check didn't find any CAA records for w.x.y.z, then that means
		// that we checked the CAA records for x.y.z, y.z, and z as well, and are
		// covered for any issuance for those names.
		if present == "false" {
			labels := strings.Split(name, ".")
			for i := 1; i < len(labels)-1; i++ {
				tailName := strings.Join(labels[i:], ".")
				issuances[tailName] = removeCoveredTimestamps(issuances[tailName], checkTime, tolerance)
				if len(issuances[tailName]) == 0 {
					delete(issuances, tailName)
				}
			}
		}
	}

	return scanner.Err()
}

// removeCoveredTimestamps returns a new slice of timestamps which contains all
// timestamps that are *not* within 8 hours after the input timestamp.
func removeCoveredTimestamps(timestamps []time.Time, cover time.Time, tolerance time.Duration) []time.Time {
	r := make([]time.Time, 0)
	for _, ts := range timestamps {
		// Copy the timestamp into the results slice if it is before the covering
		// timestamp, or more than 8 hours after the covering timestamp (i.e. if
		// it is *not* covered by the covering timestamp).
		diff := ts.Sub(cover)
		if diff < -tolerance || diff > 8*time.Hour+tolerance {
			ts := ts
			r = append(r, ts)
		}
	}
	return r
}

// formatErrors returns nil if the input map is empty. Otherwise, it returns an
// error containing a listing of every name and issuance time that was not
// covered by a CAA check.
func formatErrors(remaining map[string][]time.Time) string {
	if len(remaining) == 0 {
		return ""
	}

	messages := make([]string, len(remaining))
	for name, timestamps := range remaining {
		for _, timestamp := range timestamps {
			messages = append(messages, fmt.Sprintf("%v: %s", timestamp, name))
		}
	}

	sort.Strings(messages)
	return strings.Join(messages, "\n")
}

func main() {
	logStdoutLevel := flag.Int("stdout-level", 6, "Minimum severity of messages to send to stdout")
	logSyslogLevel := flag.Int("syslog-level", 6, "Minimum severity of messages to send to syslog")
	raLog := flag.String("ra-log", "", "Path to a single boulder-ra log file")
	vaLogs := flag.String("va-logs", "", "List of paths to boulder-va logs, separated by commas")
	timeTolerance := flag.Duration("time-tolerance", 0, "How much slop to allow when comparing timestamps for ordering")
	earliestFlag := flag.String("earliest", "", "Deprecated.")
	latestFlag := flag.String("latest", "", "Deprecated.")

	flag.Parse()

	logger := cmd.NewLogger(cmd.SyslogConfig{
		StdoutLevel: *logStdoutLevel,
		SyslogLevel: *logSyslogLevel,
	})

	if *timeTolerance < 0 {
		cmd.Fail("value of -time-tolerance must be non-negative")
	}

	if *earliestFlag != "" || *latestFlag != "" {
		logger.Info("The -earliest and -latest flags are deprecated and ignored.")
	}

	// Build a map from hostnames to times at which those names were issued for.
	// Also retrieve the earliest and latest issuance times represented in the
	// data, so we can be more efficient when examining entries from the CAA log.
	issuanceMap, earliest, latest, err := loadIssuanceLog(*raLog)
	cmd.FailOnError(err, "failed to load issuance logs")

	// Try to pare the issuance map down to nothing by removing every entry which
	// is covered by a CAA check.
	for _, vaLog := range strings.Split(*vaLogs, ",") {
		err = processCAALog(vaLog, issuanceMap, earliest, latest, *timeTolerance)
		cmd.FailOnError(err, "failed to process CAA checking logs")
	}

	errStr := formatErrors(issuanceMap)
	if errStr != "" {
		logger.AuditErrf("The following issuances were missing CAA checks:\n%s", errStr)
		os.Exit(1)
	}
}

func init() {
	cmd.RegisterCommand("caa-log-checker", main)
}
<commit_msg>Improve caa-log-checker errors (#5784)<commit_after>package notmain

import (
	"bufio"
	"compress/gzip"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/letsencrypt/boulder/cmd"
	blog "github.com/letsencrypt/boulder/log"
)

var raIssuanceLineRE = regexp.MustCompile(`Certificate request - successful JSON=(.*)`)

// TODO: Extract the "Valid for issuance: (true|false)" field too.
var vaCAALineRE = regexp.MustCompile(`Checked CAA records for ([a-z0-9-.*]+), \[Present: (true|false)`)

type issuanceEvent struct {
	SerialNumber string
	Names        []string
	Requester    int64

	issuanceTime time.Time
}

func openFile(path string) (*bufio.Scanner, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	var reader io.Reader
	reader = f
	if strings.HasSuffix(path, ".gz") {
		reader, err = gzip.NewReader(f)
		if err != nil {
			return nil, err
		}
	}
	scanner := bufio.NewScanner(reader)
	return scanner, nil
}

func parseTimestamp(line []byte) (time.Time, error) {
	datestamp, err := time.Parse(time.RFC3339, string(line[0:32]))
	if err != nil {
		return time.Time{}, err
	}
	return datestamp, nil
}

// loadIssuanceLog processes a single issuance (RA) log file. It returns a map
// of names to slices of timestamps at which certificates for those names were
// issued. It also returns the earliest and latest timestamps seen, to allow
// CAA log processing to quickly skip irrelevant entries.
func loadIssuanceLog(path string) (map[string][]time.Time, time.Time, time.Time, error) {
	scanner, err := openFile(path)
	if err != nil {
		return nil, time.Time{}, time.Time{}, fmt.Errorf("failed to open %q: %w", path, err)
	}

	linesCount := 0
	earliest := time.Time{}
	latest := time.Time{}

	issuanceMap := map[string][]time.Time{}
	for scanner.Scan() {
		line := scanner.Bytes()
		linesCount++

		matches := raIssuanceLineRE.FindSubmatch(line)
		if matches == nil {
			continue
		}
		if len(matches) != 2 {
			return nil, earliest, latest, fmt.Errorf("line %d: unexpected number of regex matches", linesCount)
		}

		var ie issuanceEvent
		err := json.Unmarshal(matches[1], &ie)
		if err != nil {
			return nil, earliest, latest, fmt.Errorf("line %d: failed to unmarshal JSON: %w", linesCount, err)
		}

		// Populate the issuance time from the syslog timestamp, rather than the
		// ResponseTime member of the JSON. This makes testing a lot simpler because
		// of how we mess with time sometimes. Given that these timestamps are
		// generated on the same system, they should be tightly coupled anyway.
		ie.issuanceTime, err = parseTimestamp(line)
		if err != nil {
			return nil, earliest, latest, fmt.Errorf("line %d: failed to parse timestamp: %w", linesCount, err)
		}

		if earliest.IsZero() || ie.issuanceTime.Before(earliest) {
			earliest = ie.issuanceTime
		}
		if latest.IsZero() || ie.issuanceTime.After(latest) {
			latest = ie.issuanceTime
		}
		for _, name := range ie.Names {
			issuanceMap[name] = append(issuanceMap[name], ie.issuanceTime)
		}
	}
	if err := scanner.Err(); err != nil {
		return nil, earliest, latest, err
	}

	return issuanceMap, earliest, latest, nil
}

// processCAALog processes a single CAA (VA) log file. It modifies the input map
// (of issuance names to times, as returned by `loadIssuanceLog`) to remove any
// timestamps which are covered by (i.e. less than 8 hours after) a CAA check
// for that name in the log file. It also prunes any names whose slice of
// issuance times becomes empty.
func processCAALog(path string, issuances map[string][]time.Time, earliest time.Time, latest time.Time, tolerance time.Duration) error {
	scanner, err := openFile(path)
	if err != nil {
		return fmt.Errorf("failed to open %q: %w", path, err)
	}

	linesCount := 0

	for scanner.Scan() {
		line := scanner.Bytes()
		linesCount++

		matches := vaCAALineRE.FindSubmatch(line)
		if matches == nil {
			continue
		}
		if len(matches) != 3 {
			return fmt.Errorf("line %d: unexpected number of regex matches", linesCount)
		}
		name := string(matches[1])
		present := string(matches[2])

		checkTime, err := parseTimestamp(line)
		if err != nil {
			return fmt.Errorf("line %d: failed to parse timestamp: %w", linesCount, err)
		}

		// Don't bother processing rows that definitely fall outside the period we
		// care about.
		if checkTime.After(latest) || checkTime.Before(earliest.Add(-8*time.Hour)) {
			continue
		}

		// TODO: Only remove covered issuance timestamps if the CAA check actually
		// said that we're allowed to issue (i.e. had "Valid for issuance: true").
		issuances[name] = removeCoveredTimestamps(issuances[name], checkTime, tolerance)
		if len(issuances[name]) == 0 {
			delete(issuances, name)
		}

		// If the CAA check didn't find any CAA records for w.x.y.z, then that means
		// that we checked the CAA records for x.y.z, y.z, and z as well, and are
		// covered for any issuance for those names.
		if present == "false" {
			labels := strings.Split(name, ".")
			for i := 1; i < len(labels)-1; i++ {
				tailName := strings.Join(labels[i:], ".")
				issuances[tailName] = removeCoveredTimestamps(issuances[tailName], checkTime, tolerance)
				if len(issuances[tailName]) == 0 {
					delete(issuances, tailName)
				}
			}
		}
	}

	return scanner.Err()
}

// removeCoveredTimestamps returns a new slice of timestamps which contains all
// timestamps that are *not* within 8 hours after the input timestamp.
func removeCoveredTimestamps(timestamps []time.Time, cover time.Time, tolerance time.Duration) []time.Time {
	r := make([]time.Time, 0)
	for _, ts := range timestamps {
		// Copy the timestamp into the results slice if it is before the covering
		// timestamp, or more than 8 hours after the covering timestamp (i.e. if
		// it is *not* covered by the covering timestamp).
		diff := ts.Sub(cover)
		if diff < -tolerance || diff > 8*time.Hour+tolerance {
			ts := ts
			r = append(r, ts)
		}
	}
	return r
}

// emitErrors returns nil if the input map is empty. Otherwise, it logs
// a line for each name and issuance time that was not covered by a CAA
// check, and return an error.
func emitErrors(log blog.Logger, remaining map[string][]time.Time) error {
	if len(remaining) == 0 {
		return nil
	}

	for name, timestamps := range remaining {
		for _, timestamp := range timestamps {
			log.Infof("CAA-checking log event not found for issuance of %s at %s", name, timestamp)
		}
	}

	return errors.New("Some CAA-checking log events not found")
}

func main() {
	logStdoutLevel := flag.Int("stdout-level", 6, "Minimum severity of messages to send to stdout")
	logSyslogLevel := flag.Int("syslog-level", 6, "Minimum severity of messages to send to syslog")
	raLog := flag.String("ra-log", "", "Path to a single boulder-ra log file")
	vaLogs := flag.String("va-logs", "", "List of paths to boulder-va logs, separated by commas")
	timeTolerance := flag.Duration("time-tolerance", 0, "How much slop to allow when comparing timestamps for ordering")
	earliestFlag := flag.String("earliest", "", "Deprecated.")
	latestFlag := flag.String("latest", "", "Deprecated.")

	flag.Parse()

	logger := cmd.NewLogger(cmd.SyslogConfig{
		StdoutLevel: *logStdoutLevel,
		SyslogLevel: *logSyslogLevel,
	})

	if *timeTolerance < 0 {
		cmd.Fail("value of -time-tolerance must be non-negative")
	}

	if *earliestFlag != "" || *latestFlag != "" {
		logger.Info("The -earliest and -latest flags are deprecated and ignored.")
	}

	// Build a map from hostnames to times at which those names were issued for.
	// Also retrieve the earliest and latest issuance times represented in the
	// data, so we can be more efficient when examining entries from the CAA log.
	issuanceMap, earliest, latest, err := loadIssuanceLog(*raLog)
	cmd.FailOnError(err, "failed to load issuance logs")

	// Try to pare the issuance map down to nothing by removing every entry which
	// is covered by a CAA check.
	for _, vaLog := range strings.Split(*vaLogs, ",") {
		err = processCAALog(vaLog, issuanceMap, earliest, latest, *timeTolerance)
		cmd.FailOnError(err, "failed to process CAA checking logs")
	}

	err = emitErrors(logger, issuanceMap)
	if err != nil {
		logger.AuditErrf("%s", err)
		os.Exit(1)
	}
}

func init() {
	cmd.RegisterCommand("caa-log-checker", main)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/mistifyio/mistify/acomm"
	flags "github.com/spf13/pflag"
)

const (
	argSep = "="
)

func main() {
	log.SetLevel(log.FatalLevel)

	var coordinator, httpAddr, taskName string
	var taskArgs []string
	var streamRequest bool
	flags.StringVarP(&coordinator, "coordinator_url", "c", "", "url of the coordinator")
	flags.StringVarP(&taskName, "task", "t", "", "task to run")
	flags.StringSliceVarP(&taskArgs, "request_arg", "a", []string{}, fmt.Sprintf("task specific argument the form 'key%svalue'. can be set multiple times", argSep))
	flags.StringVarP(&httpAddr, "http_addr", "r", ":4080", "address for http server to listen for responses and stream request data")
	flags.BoolVarP(&streamRequest, "stream", "s", false, "stream data from STDIN to provider")
	flags.Parse()

	args, err := parseTaskArgs(taskArgs)
	dieOnError(err)

	result, streamResult, respErr, err := startHTTPServer(httpAddr)
	dieOnError(err)

	dieOnError(makeRequest(coordinator, taskName, httpAddr, streamRequest, args))

	select {
	case err := <-respErr:
		dieOnError(err)
	case result := <-result:
		j, _ := json.Marshal(result)
		fmt.Println(string(j))
	case streamResult := <-streamResult:
		dieOnError(acomm.Stream(os.Stdout, streamResult))
	}
}

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

// argmap is an arbitrarilly nested map
type argmap map[string]interface{}

func (am argmap) set(keys []string, value interface{}) error {
	if len(keys) == 0 {
		fmt.Println("no more keys and returning")
		return nil
	}

	key := keys[0]

	if len(keys) == 1 {
		am[key] = value
		return nil
	}

	var m argmap
	mi, ok := am[key]
	if !ok {
		m = make(argmap)
		am[key] = m
	} else {
		m, ok = mi.(argmap)
		if !ok {
			return fmt.Errorf("intermediate nested key %s defined and not a map", key)
		}
	}

	return m.set(keys[1:], value)
}

func parseTaskArgs(taskArgs []string) (map[string]interface{}, error) {
	out := make(argmap)
	for _, in := range taskArgs {
		parts := strings.Split(in, argSep)
		if len(parts) < 2 {
			return nil, fmt.Errorf("invalid request arg: '%s'", in)
		}

		valueS := strings.Join(parts[1:], argSep)
		var value interface{}
		if arg, err := strconv.ParseInt(valueS, 10, 64); err == nil {
			value = arg
		} else if arg, err := strconv.ParseBool(valueS); err == nil {
			value = arg
		} else {
			value = valueS
		}

		keys := strings.Split(parts[0], ".")
		if err := out.set(keys, value); err != nil {
			return nil, err
		}

	}
	return out, nil
}

func startHTTPServer(addr string) (chan interface{}, chan *url.URL, chan error, error) {
	result := make(chan interface{}, 1)
	errChan := make(chan error, 1)
	stream := make(chan *url.URL, 1)

	http.HandleFunc("/response", func(w http.ResponseWriter, r *http.Request) {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			errChan <- err
			return
		}

		resp := &acomm.Response{}
		if err := json.Unmarshal(body, resp); err != nil {
			errChan <- err
			return
		}

		ack, _ := json.Marshal(&acomm.Response{})
		_, _ = w.Write(ack)

		if resp.Error != nil {
			errChan <- resp.Error
			return
		}

		if resp.StreamURL != nil {
			stream <- resp.StreamURL
		} else {
			result <- resp.Result
		}
	})
	http.HandleFunc("/stream", func(w http.ResponseWriter, r *http.Request) {
		if _, err := io.Copy(w, os.Stdin); err != nil {
			errChan <- err
			return
		}
	})

	runErr := make(chan error)
	running := time.NewTimer(time.Second)
	go func() {
		if err := http.ListenAndServe(addr, nil); err != nil {
			runErr <- err
		}
	}()

	var err error
	select {
	case <-running.C:
	case err = <-runErr:
	}

	return result, stream, errChan, err
}

func makeRequest(coordinator, taskName, httpAddr string, stream bool, taskArgs map[string]interface{}) error {
	coordinatorURL, err := url.ParseRequestURI(coordinator)
	if err != nil {
		return errors.New("invalid coordinator url")
	}

	responseHook := fmt.Sprintf("http://%s/response", httpAddr)
	streamURL := ""
	if stream {
		streamURL = fmt.Sprintf("http://%s/stream", httpAddr)
	}
	req, err := acomm.NewRequest(taskName, responseHook, streamURL, taskArgs, nil, nil)
	if err != nil {
		return err
	}

	return acomm.Send(coordinatorURL, req)
}
<commit_msg>Remove debug line that snuck in<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/mistifyio/mistify/acomm"
	flags "github.com/spf13/pflag"
)

const (
	argSep = "="
)

func main() {
	log.SetLevel(log.FatalLevel)

	var coordinator, httpAddr, taskName string
	var taskArgs []string
	var streamRequest bool
	flags.StringVarP(&coordinator, "coordinator_url", "c", "", "url of the coordinator")
	flags.StringVarP(&taskName, "task", "t", "", "task to run")
	flags.StringSliceVarP(&taskArgs, "request_arg", "a", []string{}, fmt.Sprintf("task specific argument the form 'key%svalue'. can be set multiple times", argSep))
	flags.StringVarP(&httpAddr, "http_addr", "r", ":4080", "address for http server to listen for responses and stream request data")
	flags.BoolVarP(&streamRequest, "stream", "s", false, "stream data from STDIN to provider")
	flags.Parse()

	args, err := parseTaskArgs(taskArgs)
	dieOnError(err)

	result, streamResult, respErr, err := startHTTPServer(httpAddr)
	dieOnError(err)

	dieOnError(makeRequest(coordinator, taskName, httpAddr, streamRequest, args))

	select {
	case err := <-respErr:
		dieOnError(err)
	case result := <-result:
		j, _ := json.Marshal(result)
		fmt.Println(string(j))
	case streamResult := <-streamResult:
		dieOnError(acomm.Stream(os.Stdout, streamResult))
	}
}

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

// argmap is an arbitrarilly nested map
type argmap map[string]interface{}

func (am argmap) set(keys []string, value interface{}) error {
	if len(keys) == 0 {
		return nil
	}

	key := keys[0]

	if len(keys) == 1 {
		am[key] = value
		return nil
	}

	var m argmap
	mi, ok := am[key]
	if !ok {
		m = make(argmap)
		am[key] = m
	} else {
		m, ok = mi.(argmap)
		if !ok {
			return fmt.Errorf("intermediate nested key %s defined and not a map", key)
		}
	}

	return m.set(keys[1:], value)
}

func parseTaskArgs(taskArgs []string) (map[string]interface{}, error) {
	out := make(argmap)
	for _, in := range taskArgs {
		parts := strings.Split(in, argSep)
		if len(parts) < 2 {
			return nil, fmt.Errorf("invalid request arg: '%s'", in)
		}

		valueS := strings.Join(parts[1:], argSep)
		var value interface{}
		if arg, err := strconv.ParseInt(valueS, 10, 64); err == nil {
			value = arg
		} else if arg, err := strconv.ParseBool(valueS); err == nil {
			value = arg
		} else {
			value = valueS
		}

		keys := strings.Split(parts[0], ".")
		if err := out.set(keys, value); err != nil {
			return nil, err
		}

	}
	return out, nil
}

func startHTTPServer(addr string) (chan interface{}, chan *url.URL, chan error, error) {
	result := make(chan interface{}, 1)
	errChan := make(chan error, 1)
	stream := make(chan *url.URL, 1)

	http.HandleFunc("/response", func(w http.ResponseWriter, r *http.Request) {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			errChan <- err
			return
		}

		resp := &acomm.Response{}
		if err := json.Unmarshal(body, resp); err != nil {
			errChan <- err
			return
		}

		ack, _ := json.Marshal(&acomm.Response{})
		_, _ = w.Write(ack)

		if resp.Error != nil {
			errChan <- resp.Error
			return
		}

		if resp.StreamURL != nil {
			stream <- resp.StreamURL
		} else {
			result <- resp.Result
		}
	})
	http.HandleFunc("/stream", func(w http.ResponseWriter, r *http.Request) {
		if _, err := io.Copy(w, os.Stdin); err != nil {
			errChan <- err
			return
		}
	})

	runErr := make(chan error)
	running := time.NewTimer(time.Second)
	go func() {
		if err := http.ListenAndServe(addr, nil); err != nil {
			runErr <- err
		}
	}()

	var err error
	select {
	case <-running.C:
	case err = <-runErr:
	}

	return result, stream, errChan, err
}

func makeRequest(coordinator, taskName, httpAddr string, stream bool, taskArgs map[string]interface{}) error {
	coordinatorURL, err := url.ParseRequestURI(coordinator)
	if err != nil {
		return errors.New("invalid coordinator url")
	}

	responseHook := fmt.Sprintf("http://%s/response", httpAddr)
	streamURL := ""
	if stream {
		streamURL = fmt.Sprintf("http://%s/stream", httpAddr)
	}
	req, err := acomm.NewRequest(taskName, responseHook, streamURL, taskArgs, nil, nil)
	if err != nil {
		return err
	}

	return acomm.Send(coordinatorURL, req)
}
<|endoftext|>


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

package cmd

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

	"github.com/ghchinoy/cectl/ce"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// listFormualInstancesCmd represents the listFormualInstances command
var listFormulaInstancesCmd = &cobra.Command{
	Use:   "instances <id>",
	Short: "List Instances associated with a specific Formula",
	Long:  `Retrieve a list of all instances associated with a particular formula template.`,
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) < 1 {
			fmt.Println("must supply an ID of a Formula")
			os.Exit(1)
		}

		if !viper.IsSet(profile + ".base") {
			fmt.Println("Can't find info for profile", profile)
			os.Exit(1)
		}

		base := viper.Get(profile + ".base")
		user := viper.Get(profile + ".user")
		org := viper.Get(profile + ".org")

		url := fmt.Sprintf("%s%s",
			base,
			fmt.Sprintf(ce.FormulaInstancesURIFormat, args[0]),
		)
		auth := fmt.Sprintf("User %s, Organization %s", user, org)

		client := &http.Client{}
		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			fmt.Println("Can't construct request", err.Error())
			os.Exit(1)
		}
		req.Header.Add("Authorization", auth)
		req.Header.Add("Accept", "application/json")
		req.Header.Add("Content-Type", "application/json")
		resp, err := client.Do(req)
		if err != nil {
			fmt.Println("Cannot process response", err.Error())
			os.Exit(1)
		}
		bodybytes, err := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()

		if outputJSON {
			fmt.Printf("%s\n", bodybytes)
			return
		}

		if resp.StatusCode == 200 {
			data := [][]string{}

			var instances []ce.FormulaInstance
			err = json.Unmarshal(bodybytes, &instances)
			for _, v := range instances {
				data = append(data, []string{
					strconv.Itoa(v.ID),
					v.Name,
					strconv.FormatBool(v.Active),
					fmt.Sprintf("%v %s", v.Formula.ID, v.Formula.Name),
					v.CreatedDate.String(),
				})
			}

			table := tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"ID", "Instance", "active", "Formula", "Created"})
			table.SetBorder(false)
			table.AppendBulk(data)
			table.Render()
		}

	},
}

func init() {
	formulasCmd.AddCommand(listFormulaInstancesCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// listFormualInstancesCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// listFormualInstancesCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

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

package cmd

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

	"github.com/ghchinoy/cectl/ce"
	"github.com/olekukonko/tablewriter"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// listFormualInstancesCmd represents the listFormualInstances command
var listFormulaInstancesCmd = &cobra.Command{
	Use:   "instances <id>",
	Short: "List Instances associated with a specific Formula",
	Long:  `Retrieve a list of all instances associated with a particular formula template.`,
	Run: func(cmd *cobra.Command, args []string) {

		if len(args) < 1 {
			fmt.Println("must supply an ID of a Formula")
			os.Exit(1)
		}

		if !viper.IsSet(profile + ".base") {
			fmt.Println("Can't find info for profile", profile)
			os.Exit(1)
		}

		base := viper.Get(profile + ".base")
		user := viper.Get(profile + ".user")
		org := viper.Get(profile + ".org")

		url := fmt.Sprintf("%s%s",
			base,
			fmt.Sprintf(ce.FormulaInstancesURIFormat, args[0]),
		)
		auth := fmt.Sprintf("User %s, Organization %s", user, org)

		client := &http.Client{}
		req, err := http.NewRequest("GET", url, nil)
		if err != nil {
			fmt.Println("Can't construct request", err.Error())
			os.Exit(1)
		}
		req.Header.Add("Authorization", auth)
		req.Header.Add("Accept", "application/json")
		req.Header.Add("Content-Type", "application/json")
		resp, err := client.Do(req)
		if err != nil {
			fmt.Println("Cannot process response", err.Error())
			os.Exit(1)
		}
		bodybytes, err := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()

		if outputJSON {
			fmt.Printf("%s\n", bodybytes)
			return
		}

		if resp.StatusCode == 200 {
			data := [][]string{}

			var instances []ce.FormulaInstance
			err = json.Unmarshal(bodybytes, &instances)
			for _, v := range instances {

				var configs []string
				if c, ok := v.Configuration.(map[string]interface{}); ok {
					for k, v := range c {
						configs = append(configs, fmt.Sprintf("%s:%s", k, v))
					}
				}

				data = append(data, []string{
					strconv.Itoa(v.ID),
					v.Name,
					strconv.FormatBool(v.Active),
					fmt.Sprintf("%v %s", v.Formula.ID, v.Formula.Name),
					strings.Join(configs, ", "),
					v.CreatedDate.String(),
				})
			}

			table := tablewriter.NewWriter(os.Stdout)
			table.SetHeader([]string{"ID", "Instance", "active", "Formula", "Configuration", "Created"})
			table.SetBorder(false)
			table.AppendBulk(data)
			table.Render()
		}

	},
}

func init() {
	formulasCmd.AddCommand(listFormulaInstancesCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// listFormualInstancesCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// listFormualInstancesCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

}
<|endoftext|>