<commit_before>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

//go:build go1.18
// +build go1.18

package syntax_test

import (
	"io"
	"os/exec"
	"strings"
	"testing"

	"mvdan.cc/sh/v3/syntax"
)

func FuzzQuote(f *testing.F) {
	// Keep in sync with ExampleQuote.
	f.Add("foo")
	f.Add("bar $baz")
	f.Add(`"won't"`)
	f.Add(`~/home`)
	f.Add("#1304")
	f.Add("name=value")
	f.Add(`glob-*`)
	f.Add("invalid-\xe2'")
	f.Add("nonprint-\x0b\x1b")
	f.Fuzz(func(t *testing.T, s string) {
		quoted, ok := syntax.Quote(s)
		if !ok {
			// Contains a null byte; not interesting.
			t.Skip()
		}
		// Beware that this might run arbitrary code
		// if Quote is too naive and allows ';' or '$'.
		//
		// Also note that this fuzzing would not catch '=',
		// as we don't use the quoted string as a first argument
		// to avoid running random commands.
		//
		// We could consider ways to fully sandbox the bash process,
		// but for now that feels overkill.
		out, err := exec.Command("bash", "-c", "printf %s "+quoted).CombinedOutput()
		if err != nil {
			t.Fatalf("bash error on %q quoted as %s: %v: %s", s, quoted, err, out)
		}
		want, got := s, string(out)
		if want != got {
			t.Fatalf("output mismatch on %q quoted as %s: got %q (len=%d)", want, quoted, got, len(got))
		}
	})
}

func FuzzParsePrint(f *testing.F) {
	// TODO: turn these into separate option parameters
	var zeroParserOpts uint8
	var zeroPrinterOpts uint16

	// TODO: probably use f.Add on table-driven test cases too?
	// In the past, any crashers found by go-fuzz got put there.

	f.Add("echo foo", zeroParserOpts, zeroPrinterOpts)
	f.Add("'foo' \"bar\" $'baz'", zeroParserOpts, zeroPrinterOpts)
	f.Add("if foo; then bar; baz; fi", zeroParserOpts, zeroPrinterOpts)
	f.Add("{ (foo; bar); baz; }", zeroParserOpts, zeroPrinterOpts)
	f.Add("$foo ${bar} ${baz[@]} $", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo >bar <<EOF\nbaz\nEOF", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo=bar baz=(x y z)", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo && bar || baz", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo | bar # baz", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo \\\n bar \\\\ba\\z", zeroParserOpts, zeroPrinterOpts)

	f.Fuzz(func(t *testing.T, src string, parserOpts uint8, printerOpts uint16) {
		// Below are the bit masks for parserOpts and printerOpts.
		// Most masks are a single bit, for boolean options.
		const (
			// parser
			// TODO: also fuzz StopAt
			maskLangVariant  = 0b0000_0011 // two bits; 0-3 matching the iota
			maskKeepComments = 0b0000_0100
			maskSimplify     = 0b0000_1000 // pretend it's a parser option

			// printer
			maskIndent           = 0b0000_0000_0000_1111 // three bits; 0-15
			maskBinaryNextLine   = 0b0000_0000_0001_0000
			maskSwitchCaseIndent = 0b0000_0000_0010_0000
			maskSpaceRedirects   = 0b0000_0000_0100_0000
			maskKeepPadding      = 0b0000_0000_1000_0000
			maskMinify           = 0b0000_0001_0000_0000
			maskSingleLine       = 0b0000_0010_0000_0000
			maskFunctionNextLine = 0b0000_0100_0000_0000
		)

		parser := syntax.NewParser()
		lang := syntax.LangVariant(parserOpts & maskLangVariant) // range 0-3
		syntax.Variant(lang)(parser)
		syntax.KeepComments(parserOpts&maskKeepComments != 0)(parser)

		prog, err := parser.Parse(strings.NewReader(src), "")
		if err != nil {
			t.Skip() // not valid shell syntax
		}

		if parserOpts&maskSimplify != 0 {
			syntax.Simplify(prog)
		}

		printer := syntax.NewPrinter()
		indent := uint(printerOpts & maskIndent) // range 0-15
		syntax.Indent(indent)(printer)
		syntax.BinaryNextLine(printerOpts&maskBinaryNextLine != 0)(printer)
		syntax.SwitchCaseIndent(printerOpts&maskSwitchCaseIndent != 0)(printer)
		syntax.SpaceRedirects(printerOpts&maskSpaceRedirects != 0)(printer)
		syntax.KeepPadding(printerOpts&maskKeepPadding != 0)(printer)
		syntax.Minify(printerOpts&maskMinify != 0)(printer)
		syntax.SingleLine(printerOpts&maskSingleLine != 0)(printer)
		syntax.FunctionNextLine(printerOpts&maskFunctionNextLine != 0)(printer)

		if err := printer.Print(io.Discard, prog); err != nil {
			t.Skip() // e.g. invalid option
		}
	})
}
<commit_msg>syntax: skip FuzzQuote if bash isn't present<commit_after>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

//go:build go1.18
// +build go1.18

package syntax_test

import (
	"io"
	"os/exec"
	"strings"
	"testing"

	"mvdan.cc/sh/v3/syntax"
)

func FuzzQuote(f *testing.F) {
	if _, err := exec.LookPath("bash"); err != nil {
		f.Skipf("requires bash to verify quoted strings")
	}

	// Keep in sync with ExampleQuote.
	f.Add("foo")
	f.Add("bar $baz")
	f.Add(`"won't"`)
	f.Add(`~/home`)
	f.Add("#1304")
	f.Add("name=value")
	f.Add(`glob-*`)
	f.Add("invalid-\xe2'")
	f.Add("nonprint-\x0b\x1b")
	f.Fuzz(func(t *testing.T, s string) {
		quoted, ok := syntax.Quote(s)
		if !ok {
			// Contains a null byte; not interesting.
			t.Skip()
		}
		// Beware that this might run arbitrary code
		// if Quote is too naive and allows ';' or '$'.
		//
		// Also note that this fuzzing would not catch '=',
		// as we don't use the quoted string as a first argument
		// to avoid running random commands.
		//
		// We could consider ways to fully sandbox the bash process,
		// but for now that feels overkill.
		out, err := exec.Command("bash", "-c", "printf %s "+quoted).CombinedOutput()
		if err != nil {
			t.Fatalf("bash error on %q quoted as %s: %v: %s", s, quoted, err, out)
		}
		want, got := s, string(out)
		if want != got {
			t.Fatalf("output mismatch on %q quoted as %s: got %q (len=%d)", want, quoted, got, len(got))
		}
	})
}

func FuzzParsePrint(f *testing.F) {
	// TODO: turn these into separate option parameters
	var zeroParserOpts uint8
	var zeroPrinterOpts uint16

	// TODO: probably use f.Add on table-driven test cases too?
	// In the past, any crashers found by go-fuzz got put there.

	f.Add("echo foo", zeroParserOpts, zeroPrinterOpts)
	f.Add("'foo' \"bar\" $'baz'", zeroParserOpts, zeroPrinterOpts)
	f.Add("if foo; then bar; baz; fi", zeroParserOpts, zeroPrinterOpts)
	f.Add("{ (foo; bar); baz; }", zeroParserOpts, zeroPrinterOpts)
	f.Add("$foo ${bar} ${baz[@]} $", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo >bar <<EOF\nbaz\nEOF", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo=bar baz=(x y z)", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo && bar || baz", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo | bar # baz", zeroParserOpts, zeroPrinterOpts)
	f.Add("foo \\\n bar \\\\ba\\z", zeroParserOpts, zeroPrinterOpts)

	f.Fuzz(func(t *testing.T, src string, parserOpts uint8, printerOpts uint16) {
		// Below are the bit masks for parserOpts and printerOpts.
		// Most masks are a single bit, for boolean options.
		const (
			// parser
			// TODO: also fuzz StopAt
			maskLangVariant  = 0b0000_0011 // two bits; 0-3 matching the iota
			maskKeepComments = 0b0000_0100
			maskSimplify     = 0b0000_1000 // pretend it's a parser option

			// printer
			maskIndent           = 0b0000_0000_0000_1111 // three bits; 0-15
			maskBinaryNextLine   = 0b0000_0000_0001_0000
			maskSwitchCaseIndent = 0b0000_0000_0010_0000
			maskSpaceRedirects   = 0b0000_0000_0100_0000
			maskKeepPadding      = 0b0000_0000_1000_0000
			maskMinify           = 0b0000_0001_0000_0000
			maskSingleLine       = 0b0000_0010_0000_0000
			maskFunctionNextLine = 0b0000_0100_0000_0000
		)

		parser := syntax.NewParser()
		lang := syntax.LangVariant(parserOpts & maskLangVariant) // range 0-3
		syntax.Variant(lang)(parser)
		syntax.KeepComments(parserOpts&maskKeepComments != 0)(parser)

		prog, err := parser.Parse(strings.NewReader(src), "")
		if err != nil {
			t.Skip() // not valid shell syntax
		}

		if parserOpts&maskSimplify != 0 {
			syntax.Simplify(prog)
		}

		printer := syntax.NewPrinter()
		indent := uint(printerOpts & maskIndent) // range 0-15
		syntax.Indent(indent)(printer)
		syntax.BinaryNextLine(printerOpts&maskBinaryNextLine != 0)(printer)
		syntax.SwitchCaseIndent(printerOpts&maskSwitchCaseIndent != 0)(printer)
		syntax.SpaceRedirects(printerOpts&maskSpaceRedirects != 0)(printer)
		syntax.KeepPadding(printerOpts&maskKeepPadding != 0)(printer)
		syntax.Minify(printerOpts&maskMinify != 0)(printer)
		syntax.SingleLine(printerOpts&maskSingleLine != 0)(printer)
		syntax.FunctionNextLine(printerOpts&maskFunctionNextLine != 0)(printer)

		if err := printer.Print(io.Discard, prog); err != nil {
			t.Skip() // e.g. invalid option
		}
	})
}
<|endoftext|>


<commit_before>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * 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 data

// Object represents any JavaScript value (primitive, object, etc.).
type Object interface {
	/// Any Object is a valid Value.
	Value

	// Proto returns the prototype (parent) object for this object.
	// N.B. this is the value returned by Object.getPrototypeOf(foo)
	// (and by foo.__proto__, if not overridden); it is unrelated to
	// the .prototype property on Functions.
	Proto() Object

	// DefineOwnProperty creates a new property (or updates an
	// existing one, if possible) with the specified property
	// descriptor.
	DefineOwnProperty(key string, pd Property) *NativeError

	// GetOwnProperty returns the property descriptor for the
	// specified key and ok == true (if a property with the specified
	// key exists), or ok == false (if it doesn't).
	GetOwnProperty(key string) (pd Property, ok bool)

	// Get returns the current value of the given property or an
	// NativeError if that was not possible.
	Get(key string) (Value, *NativeError)

	// Set sets the given property to the specified value or returns
	// an NativeError if that was not possible.
	Set(key string, value Value) *NativeError

	// Delete attempts to remove the specified property.  If the
	// property exists but can't be removed for some reason an
	// NativeError is returned.  (Removing a non-existing property
	// "succeeds" silently.)
	Delete(key string) *NativeError

	// OwnPropertyKeys returns the list of (own) property keys as a
	// slice of strings.
	OwnPropertyKeys() []string

	// HasOwnProperty returns true if the specified property key
	// exists on the object itself.
	HasOwnProperty(string) bool

	// HasProperty returns true if the specified property key
	// exists on the object or its prototype chain.
	HasProperty(string) bool
}

/********************************************************************/

// object represents typical plain old JavaScript objects with
// prototype, properties, etc.; this struct is also embedded in other,
// less-plain object types like Array.
type object struct {
	owner      *Owner
	proto      Object
	properties map[string]Property
	f          bool
}

// *object must satisfy Object.
var _ Object = (*object)(nil)

// Type always returns OBJECT for regular objects.
func (object) Type() Type {
	return OBJECT
}

// Typeof always returns "object" for regular objects.
func (object) Typeof() string {
	return "object"
}

// IsPrimitive always returns false for regular objects.
func (object) IsPrimitive() bool {
	return false
}

// Proto returns the prototype (parent) object for this object.
func (obj object) Proto() Object {
	return obj.proto
}

// DefineOwnProperty creates a new property (or updates an existing
// one, if possible) with the specified property descriptor.
func (obj *object) DefineOwnProperty(key string, pd Property) *NativeError {
	// FIXME: perm / configurability checks!
	if pd.Value == nil {
		pd.Value = Undefined{}
	}
	obj.properties[key] = pd
	return nil
}

// GetOwnProperty returns the property descriptor for the specified
// key and ok == true (if a property with the specified key exists),
// or ok == false (if it doesn't).
//
// FIXME: this needs to be redefined more carefully on Array,
// BoxedString, etc.
func (obj *object) GetOwnProperty(key string) (pd Property, ok bool) {
	// FIXME: perm check?
	pd, ok = obj.properties[key]
	return pd, ok
}

// Get returns the current value of the given property or an
// NativeError if that was not possible.
//
// FIXME: this needs to be redefined more carefully on Array,
// BoxedString, etc.
func (obj object) Get(key string) (Value, *NativeError) {
	pd, ok := obj.properties[key]
	// FIXME: permissions check for property readability goes here
	if ok {
		return pd.Value, nil
	}
	// Try the prototype?
	proto := obj.Proto()
	if proto != nil {
		return proto.Get(key)
	}
	return Undefined{}, nil
}

// Set sets the given property to the specified value or returns an
// NativeError if that was not possible.
func (obj *object) Set(key string, value Value) *NativeError {
	pd, ok := obj.properties[key]
	if !ok { // Creating new property
		pd := Property{
			Value: value,
			Owner: obj.owner, // FIXME: should this be caller?
			W:     true,
			E:     true,
			C:     true,
			R:     true,
			I:     false,
		}
		return obj.DefineOwnProperty(key, pd)
	}
	// Updating existing property
	// FIXME: permissions check for property writeability goes here
	// FIXME: recurse if necessary
	pd.Value = value
	obj.properties[key] = pd
	return nil
}

// Delete removes the specified property if possible.
//
// FIXME: perm / immutability checks!
func (obj *object) Delete(key string) *NativeError {
	delete(obj.properties, key)
	return nil
}

// OwnPropertyKeys returns the list of (own) property keys as a slice
// of strings.
func (obj *object) OwnPropertyKeys() []string {
	keys := make([]string, len(obj.properties))
	i := 0
	for k := range obj.properties {
		keys[i] = k
		i++
	}
	return keys
}

// HasOwnProperty returns true if the specified property key exists on
// the object itself.
func (obj *object) HasOwnProperty(key string) bool {
	_, exists := obj.properties[key]
	return exists
}

// HasProperty returns true if the specified property key exists on
// the object or its prototype chain.
func (obj *object) HasProperty(key string) bool {
	return obj.HasOwnProperty(key) ||
		obj.proto != nil && obj.proto.HasProperty(key)
}

// ToBoolean always returns true for regular objects.
func (object) ToBoolean() Boolean {
	return true
}

// ToNumber returns the numeric equivalent of the object.
//
// BUG(cpcallen): object.ToNumber is not strictly compliant with ES5.1
// spec; it just returns .ToString().ToNumber().
func (obj object) ToNumber() Number {
	return obj.ToString().ToNumber()
}

// ToString returns a string representation of the object.  By default
// this is "[object Object]" for plain objects.
//
// BUG(cpcallen): object.ToString should call a user-code toString()
// method if present.
func (object) ToString() String {
	return "[object Object]"
}

// ToPrimitive defaults to ToNumber on objects.
//
// BUG(cpcallen): object.ToPrimitive should prefer to return the
// result of ToString() on date objects.
func (obj *object) ToPrimitive() Value {
	return obj.ToNumber()
}

// NewObject creates a new object with the specified owner and
// prototype, initialises it as appropriate, and returns a pointer to
// the newly-created object.
func NewObject(owner *Owner, proto Object) *object {
	var obj = new(object)
	obj.init(owner, proto)
	obj.f = true
	return obj
}

// init is an internal initialisation routine, called from New and
// also called when constructing other types of objects such as
// Arrays, Owners, etc.
func (obj *object) init(owner *Owner, proto Object) {
	obj.owner = owner
	obj.proto = proto
	obj.properties = make(map[string]Property)
}

/********************************************************************/

// Property is a property descriptor, per §8.10 of ES5.1
//     Value: The actual value of the property.
//     Owner: Who owns the property (has permission to write it)?
//     W:     Is the property writeable?
//     E:     Is the property enumerable?
//     C:     Is the property configurable?
//     R:     Is the property world-readable?
//     I:     Is the property ownership inherited on children?
type Property struct {
	Value   Value
	Owner   *Owner
	W, E, C bool
	R, I    bool
}

// FromProeprtyDescriptor implements the altorithm of the same name
// from §8.10.4 of the ES5.1 spec, but simplified because we do not
// (yet) support getters / setters, and with extra parameters for
// objet owner and prototype.
func FromPropertyDescriptor(pd Property, owner *Owner, proto Object) (desc Object, ne *NativeError) {
	desc = NewObject(owner, proto)
	ne = desc.Set("value", pd.Value)
	if ne != nil {
		return
	}
	attrs := []struct {
		flag *bool
		key  string
	}{
		{&pd.W, "writeable"},
		{&pd.E, "enumerable"},
		{&pd.C, "configurable"},
		// FIXME: either enable, or remove, once we decide
		// what flags properties will actually have:
		// {&pd.R, "readable"},
		// {&pd.I, "inheritable"},
	}
	for _, attr := range attrs {
		ne = desc.Set(attr.key, Boolean(*attr.flag))
		if ne != nil {
			return
		}
	}
	return
}

// ToProeprtyDescriptor implements the altorithm of the same name from
// §8.10.5 of the ES5.1 spec, but simplified because we do not (yet)
// support getters / setters.
func ToPropertyDescriptor(obj Object) (pd Property, ne *NativeError) {
	pd.Value, ne = obj.Get("value")
	if ne != nil {
		return
	}
	// FIXME: set owner
	pd.Owner = nil
	attrs := []struct {
		flag *bool
		key  string
	}{
		{&pd.W, "writeable"},
		{&pd.E, "enumerable"},
		{&pd.C, "configurable"},
		// FIXME: either enable, or remove, once we decide what flags
		// properties will actually have:
		// {&pd.R, "readable"},
		// {&pd.I, "inheritable"},
	}
	for _, attr := range attrs {
		var v Value
		v, ne = obj.Get(attr.key)
		if ne != nil {
			return
		}
		*(attr.flag) = bool(v.ToBoolean())
	}
	return
}
<commit_msg>Remove lint<commit_after>/* Copyright 2017 Google Inc.
 * https://github.com/NeilFraser/CodeCity
 *
 * 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 data

// Object represents any JavaScript value (primitive, object, etc.).
type Object interface {
	/// Any Object is a valid Value.
	Value

	// Proto returns the prototype (parent) object for this object.
	// N.B. this is the value returned by Object.getPrototypeOf(foo)
	// (and by foo.__proto__, if not overridden); it is unrelated to
	// the .prototype property on Functions.
	Proto() Object

	// DefineOwnProperty creates a new property (or updates an
	// existing one, if possible) with the specified property
	// descriptor.
	DefineOwnProperty(key string, pd Property) *NativeError

	// GetOwnProperty returns the property descriptor for the
	// specified key and ok == true (if a property with the specified
	// key exists), or ok == false (if it doesn't).
	GetOwnProperty(key string) (pd Property, ok bool)

	// Get returns the current value of the given property or an
	// NativeError if that was not possible.
	Get(key string) (Value, *NativeError)

	// Set sets the given property to the specified value or returns
	// an NativeError if that was not possible.
	Set(key string, value Value) *NativeError

	// Delete attempts to remove the specified property.  If the
	// property exists but can't be removed for some reason an
	// NativeError is returned.  (Removing a non-existing property
	// "succeeds" silently.)
	Delete(key string) *NativeError

	// OwnPropertyKeys returns the list of (own) property keys as a
	// slice of strings.
	OwnPropertyKeys() []string

	// HasOwnProperty returns true if the specified property key
	// exists on the object itself.
	HasOwnProperty(string) bool

	// HasProperty returns true if the specified property key
	// exists on the object or its prototype chain.
	HasProperty(string) bool
}

/********************************************************************/

// object represents typical plain old JavaScript objects with
// prototype, properties, etc.; this struct is also embedded in other,
// less-plain object types like Array.
type object struct {
	owner      *Owner
	proto      Object
	properties map[string]Property
	f          bool
}

// *object must satisfy Object.
var _ Object = (*object)(nil)

// Type always returns OBJECT for regular objects.
func (object) Type() Type {
	return OBJECT
}

// Typeof always returns "object" for regular objects.
func (object) Typeof() string {
	return "object"
}

// IsPrimitive always returns false for regular objects.
func (object) IsPrimitive() bool {
	return false
}

// Proto returns the prototype (parent) object for this object.
func (obj object) Proto() Object {
	return obj.proto
}

// DefineOwnProperty creates a new property (or updates an existing
// one, if possible) with the specified property descriptor.
func (obj *object) DefineOwnProperty(key string, pd Property) *NativeError {
	// FIXME: perm / configurability checks!
	if pd.Value == nil {
		pd.Value = Undefined{}
	}
	obj.properties[key] = pd
	return nil
}

// GetOwnProperty returns the property descriptor for the specified
// key and ok == true (if a property with the specified key exists),
// or ok == false (if it doesn't).
//
// FIXME: this needs to be redefined more carefully on Array,
// BoxedString, etc.
func (obj *object) GetOwnProperty(key string) (pd Property, ok bool) {
	// FIXME: perm check?
	pd, ok = obj.properties[key]
	return pd, ok
}

// Get returns the current value of the given property or an
// NativeError if that was not possible.
//
// FIXME: this needs to be redefined more carefully on Array,
// BoxedString, etc.
func (obj object) Get(key string) (Value, *NativeError) {
	pd, ok := obj.properties[key]
	// FIXME: permissions check for property readability goes here
	if ok {
		return pd.Value, nil
	}
	// Try the prototype?
	proto := obj.Proto()
	if proto != nil {
		return proto.Get(key)
	}
	return Undefined{}, nil
}

// Set sets the given property to the specified value or returns an
// NativeError if that was not possible.
func (obj *object) Set(key string, value Value) *NativeError {
	pd, ok := obj.properties[key]
	if !ok { // Creating new property
		pd := Property{
			Value: value,
			Owner: obj.owner, // FIXME: should this be caller?
			W:     true,
			E:     true,
			C:     true,
			R:     true,
			I:     false,
		}
		return obj.DefineOwnProperty(key, pd)
	}
	// Updating existing property
	// FIXME: permissions check for property writeability goes here
	// FIXME: recurse if necessary
	pd.Value = value
	obj.properties[key] = pd
	return nil
}

// Delete removes the specified property if possible.
//
// FIXME: perm / immutability checks!
func (obj *object) Delete(key string) *NativeError {
	delete(obj.properties, key)
	return nil
}

// OwnPropertyKeys returns the list of (own) property keys as a slice
// of strings.
func (obj *object) OwnPropertyKeys() []string {
	keys := make([]string, len(obj.properties))
	i := 0
	for k := range obj.properties {
		keys[i] = k
		i++
	}
	return keys
}

// HasOwnProperty returns true if the specified property key exists on
// the object itself.
func (obj *object) HasOwnProperty(key string) bool {
	_, exists := obj.properties[key]
	return exists
}

// HasProperty returns true if the specified property key exists on
// the object or its prototype chain.
func (obj *object) HasProperty(key string) bool {
	return obj.HasOwnProperty(key) ||
		obj.proto != nil && obj.proto.HasProperty(key)
}

// ToBoolean always returns true for regular objects.
func (object) ToBoolean() Boolean {
	return true
}

// ToNumber returns the numeric equivalent of the object.
//
// BUG(cpcallen): object.ToNumber is not strictly compliant with ES5.1
// spec; it just returns .ToString().ToNumber().
func (obj object) ToNumber() Number {
	return obj.ToString().ToNumber()
}

// ToString returns a string representation of the object.  By default
// this is "[object Object]" for plain objects.
//
// BUG(cpcallen): object.ToString should call a user-code toString()
// method if present.
func (object) ToString() String {
	return "[object Object]"
}

// ToPrimitive defaults to ToNumber on objects.
//
// BUG(cpcallen): object.ToPrimitive should prefer to return the
// result of ToString() on date objects.
func (obj *object) ToPrimitive() Value {
	return obj.ToNumber()
}

// NewObject creates a new object with the specified owner and
// prototype, initialises it as appropriate, and returns a pointer to
// the newly-created object.
func NewObject(owner *Owner, proto Object) *object {
	var obj = new(object)
	obj.init(owner, proto)
	obj.f = true
	return obj
}

// init is an internal initialisation routine, called from New and
// also called when constructing other types of objects such as
// Arrays, Owners, etc.
func (obj *object) init(owner *Owner, proto Object) {
	obj.owner = owner
	obj.proto = proto
	obj.properties = make(map[string]Property)
}

/********************************************************************/

// Property is a property descriptor, per §8.10 of ES5.1
//     Value: The actual value of the property.
//     Owner: Who owns the property (has permission to write it)?
//     W:     Is the property writeable?
//     E:     Is the property enumerable?
//     C:     Is the property configurable?
//     R:     Is the property world-readable?
//     I:     Is the property ownership inherited on children?
type Property struct {
	Value   Value
	Owner   *Owner
	W, E, C bool
	R, I    bool
}

// FromPropertyDescriptor implements the altorithm of the same name
// from §8.10.4 of the ES5.1 spec, but simplified because we do not
// (yet) support getters / setters, and with extra parameters for
// objet owner and prototype.
func FromPropertyDescriptor(pd Property, owner *Owner, proto Object) (desc Object, ne *NativeError) {
	desc = NewObject(owner, proto)
	ne = desc.Set("value", pd.Value)
	if ne != nil {
		return
	}
	attrs := []struct {
		flag *bool
		key  string
	}{
		{&pd.W, "writeable"},
		{&pd.E, "enumerable"},
		{&pd.C, "configurable"},
		// FIXME: either enable, or remove, once we decide
		// what flags properties will actually have:
		// {&pd.R, "readable"},
		// {&pd.I, "inheritable"},
	}
	for _, attr := range attrs {
		ne = desc.Set(attr.key, Boolean(*attr.flag))
		if ne != nil {
			return
		}
	}
	return
}

// ToPropertyDescriptor implements the altorithm of the same name from
// §8.10.5 of the ES5.1 spec, but simplified because we do not (yet)
// support getters / setters.
func ToPropertyDescriptor(obj Object) (pd Property, ne *NativeError) {
	pd.Value, ne = obj.Get("value")
	if ne != nil {
		return
	}
	// FIXME: set owner
	pd.Owner = nil
	attrs := []struct {
		flag *bool
		key  string
	}{
		{&pd.W, "writeable"},
		{&pd.E, "enumerable"},
		{&pd.C, "configurable"},
		// FIXME: either enable, or remove, once we decide what flags
		// properties will actually have:
		// {&pd.R, "readable"},
		// {&pd.I, "inheritable"},
	}
	for _, attr := range attrs {
		var v Value
		v, ne = obj.Get(attr.key)
		if ne != nil {
			return
		}
		*(attr.flag) = bool(v.ToBoolean())
	}
	return
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package nwo_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"syscall"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/hyperledger/fabric/integration/nwo"
	"github.com/hyperledger/fabric/integration/nwo/commands"
	"github.com/tedsuo/ifrit"
	yaml "gopkg.in/yaml.v2"
)

var _ = Describe("Network", func() {
	var (
		client  *docker.Client
		tempDir string
	)

	BeforeEach(func() {
		var err error
		tempDir, err = ioutil.TempDir("", "nwo")
		Expect(err).NotTo(HaveOccurred())

		client, err = docker.NewClientFromEnv()
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		os.RemoveAll(tempDir)
	})

	Describe("solo network", func() {
		var network *nwo.Network
		var process ifrit.Process

		BeforeEach(func() {
			soloBytes, err := ioutil.ReadFile("solo.yaml")
			Expect(err).NotTo(HaveOccurred())

			var config *nwo.Config
			err = yaml.Unmarshal(soloBytes, &config)
			Expect(err).NotTo(HaveOccurred())
			network = nwo.New(config, tempDir, client, 44444, components)

			// Generate config and bootstrap the network
			network.GenerateConfigTree()
			network.Bootstrap()

			// Start all of the fabric processes
			networkRunner := network.NetworkGroupRunner()
			process = ifrit.Invoke(networkRunner)
			Eventually(process.Ready()).Should(BeClosed())
		})

		AfterEach(func() {
			// Shutodwn processes and cleanup
			process.Signal(syscall.SIGTERM)
			Eventually(process.Wait(), time.Minute).Should(Receive())
			network.Cleanup()
		})

		It("deploys and executes chaincode (simple)", func() {
			orderer := network.Orderer("orderer0")
			peer := network.Peer("org1", "peer2")

			chaincode := nwo.Chaincode{
				Name:    "mycc",
				Version: "0.0",
				Path:    "github.com/hyperledger/fabric/integration/chaincode/simple/cmd",
				Ctor:    `{"Args":["init","a","100","b","200"]}`,
				Policy:  `AND ('Org1ExampleCom.member','Org2ExampleCom.member')`,
			}

			network.CreateAndJoinChannels(orderer)
			nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
			RunQueryInvokeQuery(network, orderer, peer)
		})
	})

	Describe("kafka network", func() {
		var (
			config    nwo.Config
			network   *nwo.Network
			processes map[string]ifrit.Process
		)

		BeforeEach(func() {
			soloBytes, err := ioutil.ReadFile("solo.yaml")
			Expect(err).NotTo(HaveOccurred())

			err = yaml.Unmarshal(soloBytes, &config)
			Expect(err).NotTo(HaveOccurred())

			// Switch from solo to kafka
			config.Consensus.Type = "kafka"
			config.Consensus.ZooKeepers = 1
			config.Consensus.Brokers = 1

			network = nwo.New(&config, tempDir, client, 55555, components)
			network.GenerateConfigTree()
			network.Bootstrap()
			processes = map[string]ifrit.Process{}
		})

		AfterEach(func() {
			for _, p := range processes {
				p.Signal(syscall.SIGTERM)
				Eventually(p.Wait(), time.Minute).Should(Receive())
			}
			network.Cleanup()
		})

		It("deploys and executes chaincode (the hard way)", func() {
			// This demonstrates how to control the processes that make up a network.
			// If you don't care about a collection of processes (like the brokers or
			// the orderers) use the group runner to manage thos processes.
			zookeepers := []string{}
			for i := 0; i < network.Consensus.ZooKeepers; i++ {
				zk := network.ZooKeeperRunner(i)
				zookeepers = append(zookeepers, fmt.Sprintf("%s:2181", zk.Name))

				p := ifrit.Invoke(zk)
				processes[zk.Name] = p
				Eventually(p.Ready()).Should(BeClosed())
			}

			for i := 0; i < network.Consensus.Brokers; i++ {
				b := network.BrokerRunner(i, zookeepers)
				p := ifrit.Invoke(b)
				processes[b.Name] = p
				Eventually(p.Ready()).Should(BeClosed())
			}

			for _, o := range network.Orderers {
				or := network.OrdererRunner(o)
				p := ifrit.Invoke(or)
				processes[o.ID()] = p
				Eventually(p.Ready()).Should(BeClosed())
			}

			for _, peer := range network.Peers {
				pr := network.PeerRunner(peer)
				p := ifrit.Invoke(pr)
				processes[peer.ID()] = p
				Eventually(p.Ready()).Should(BeClosed())
			}

			orderer := network.Orderer("orderer0")
			testPeers := network.PeersWithChannel("testchannel")
			network.CreateChannel("testchannel", orderer, testPeers[0])
			network.JoinChannel("testchannel", orderer, testPeers...)

			chaincode := nwo.Chaincode{
				Name:    "mycc",
				Version: "0.0",
				Path:    "github.com/hyperledger/fabric/integration/chaincode/simple/cmd",
				Ctor:    `{"Args":["init","a","100","b","200"]}`,
				Policy:  `AND ('Org1ExampleCom.member','Org2ExampleCom.member')`,
			}
			nwo.InstallChaincode(network, chaincode, testPeers...)
			nwo.InstantiateChaincode(network, "testchannel", orderer, chaincode, testPeers[0])
			nwo.EnsureInstantiated(network, "testchannel", "mycc", "0.0", testPeers...)

			RunQueryInvokeQuery(network, orderer, testPeers[0])
		})
	})
})

func RunQueryInvokeQuery(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer) {
	By("querying the chaincode")
	sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeQuery{
		ChannelID: "testchannel",
		Name:      "mycc",
		Ctor:      `{"Args":["query","a"]}`,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, time.Minute).Should(gexec.Exit(0))
	Expect(sess).To(gbytes.Say("100"))

	sess, err = n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{
		ChannelID: "testchannel",
		Orderer:   n.OrdererAddress(orderer, nwo.ListenPort),
		Name:      "mycc",
		Ctor:      `{"Args":["invoke","a","b","10"]}`,
		PeerAddresses: []string{
			n.PeerAddress(n.Peer("org1", "peer1"), nwo.ListenPort),
			n.PeerAddress(n.Peer("org2", "peer2"), nwo.ListenPort),
		},
		WaitForEvent: true,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, time.Minute).Should(gexec.Exit(0))
	Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful. result: status:200"))

	sess, err = n.PeerUserSession(peer, "User1", commands.ChaincodeQuery{
		ChannelID: "testchannel",
		Name:      "mycc",
		Ctor:      `{"Args":["query","a"]}`,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, time.Minute).Should(gexec.Exit(0))
	Expect(sess).To(gbytes.Say("90"))
}
<commit_msg>[FAB-11233] Increase timeout for closing processes<commit_after>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package nwo_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"syscall"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"

	docker "github.com/fsouza/go-dockerclient"
	"github.com/hyperledger/fabric/integration/nwo"
	"github.com/hyperledger/fabric/integration/nwo/commands"
	"github.com/tedsuo/ifrit"
	yaml "gopkg.in/yaml.v2"
)

var _ = Describe("Network", func() {
	var (
		client  *docker.Client
		tempDir string
	)

	BeforeEach(func() {
		var err error
		tempDir, err = ioutil.TempDir("", "nwo")
		Expect(err).NotTo(HaveOccurred())

		client, err = docker.NewClientFromEnv()
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		os.RemoveAll(tempDir)
	})

	Describe("solo network", func() {
		var network *nwo.Network
		var process ifrit.Process

		BeforeEach(func() {
			soloBytes, err := ioutil.ReadFile("solo.yaml")
			Expect(err).NotTo(HaveOccurred())

			var config *nwo.Config
			err = yaml.Unmarshal(soloBytes, &config)
			Expect(err).NotTo(HaveOccurred())
			network = nwo.New(config, tempDir, client, 44444, components)

			// Generate config and bootstrap the network
			network.GenerateConfigTree()
			network.Bootstrap()

			// Start all of the fabric processes
			networkRunner := network.NetworkGroupRunner()
			process = ifrit.Invoke(networkRunner)
			Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed())
		})

		AfterEach(func() {
			// Shutodwn processes and cleanup
			process.Signal(syscall.SIGTERM)
			Eventually(process.Wait(), time.Minute).Should(Receive())
			network.Cleanup()
		})

		It("deploys and executes chaincode (simple)", func() {
			orderer := network.Orderer("orderer0")
			peer := network.Peer("org1", "peer2")

			chaincode := nwo.Chaincode{
				Name:    "mycc",
				Version: "0.0",
				Path:    "github.com/hyperledger/fabric/integration/chaincode/simple/cmd",
				Ctor:    `{"Args":["init","a","100","b","200"]}`,
				Policy:  `AND ('Org1ExampleCom.member','Org2ExampleCom.member')`,
			}

			network.CreateAndJoinChannels(orderer)
			nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
			RunQueryInvokeQuery(network, orderer, peer)
		})
	})

	Describe("kafka network", func() {
		var (
			config    nwo.Config
			network   *nwo.Network
			processes map[string]ifrit.Process
		)

		BeforeEach(func() {
			soloBytes, err := ioutil.ReadFile("solo.yaml")
			Expect(err).NotTo(HaveOccurred())

			err = yaml.Unmarshal(soloBytes, &config)
			Expect(err).NotTo(HaveOccurred())

			// Switch from solo to kafka
			config.Consensus.Type = "kafka"
			config.Consensus.ZooKeepers = 1
			config.Consensus.Brokers = 1

			network = nwo.New(&config, tempDir, client, 55555, components)
			network.GenerateConfigTree()
			network.Bootstrap()
			processes = map[string]ifrit.Process{}
		})

		AfterEach(func() {
			for _, p := range processes {
				p.Signal(syscall.SIGTERM)
				Eventually(p.Wait(), time.Minute).Should(Receive())
			}
			network.Cleanup()
		})

		It("deploys and executes chaincode (the hard way)", func() {
			// This demonstrates how to control the processes that make up a network.
			// If you don't care about a collection of processes (like the brokers or
			// the orderers) use the group runner to manage thos processes.
			zookeepers := []string{}
			for i := 0; i < network.Consensus.ZooKeepers; i++ {
				zk := network.ZooKeeperRunner(i)
				zookeepers = append(zookeepers, fmt.Sprintf("%s:2181", zk.Name))

				p := ifrit.Invoke(zk)
				processes[zk.Name] = p
				Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed())
			}

			for i := 0; i < network.Consensus.Brokers; i++ {
				b := network.BrokerRunner(i, zookeepers)
				p := ifrit.Invoke(b)
				processes[b.Name] = p
				Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed())
			}

			for _, o := range network.Orderers {
				or := network.OrdererRunner(o)
				p := ifrit.Invoke(or)
				processes[o.ID()] = p
				Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed())
			}

			for _, peer := range network.Peers {
				pr := network.PeerRunner(peer)
				p := ifrit.Invoke(pr)
				processes[peer.ID()] = p
				Eventually(p.Ready(), network.EventuallyTimeout).Should(BeClosed())
			}

			orderer := network.Orderer("orderer0")
			testPeers := network.PeersWithChannel("testchannel")
			network.CreateChannel("testchannel", orderer, testPeers[0])
			network.JoinChannel("testchannel", orderer, testPeers...)

			chaincode := nwo.Chaincode{
				Name:    "mycc",
				Version: "0.0",
				Path:    "github.com/hyperledger/fabric/integration/chaincode/simple/cmd",
				Ctor:    `{"Args":["init","a","100","b","200"]}`,
				Policy:  `AND ('Org1ExampleCom.member','Org2ExampleCom.member')`,
			}
			nwo.InstallChaincode(network, chaincode, testPeers...)
			nwo.InstantiateChaincode(network, "testchannel", orderer, chaincode, testPeers[0])
			nwo.EnsureInstantiated(network, "testchannel", "mycc", "0.0", testPeers...)

			RunQueryInvokeQuery(network, orderer, testPeers[0])
		})
	})
})

func RunQueryInvokeQuery(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer) {
	By("querying the chaincode")
	sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeQuery{
		ChannelID: "testchannel",
		Name:      "mycc",
		Ctor:      `{"Args":["query","a"]}`,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
	Expect(sess).To(gbytes.Say("100"))

	sess, err = n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{
		ChannelID: "testchannel",
		Orderer:   n.OrdererAddress(orderer, nwo.ListenPort),
		Name:      "mycc",
		Ctor:      `{"Args":["invoke","a","b","10"]}`,
		PeerAddresses: []string{
			n.PeerAddress(n.Peer("org1", "peer1"), nwo.ListenPort),
			n.PeerAddress(n.Peer("org2", "peer2"), nwo.ListenPort),
		},
		WaitForEvent: true,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
	Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful. result: status:200"))

	sess, err = n.PeerUserSession(peer, "User1", commands.ChaincodeQuery{
		ChannelID: "testchannel",
		Name:      "mycc",
		Ctor:      `{"Args":["query","a"]}`,
	})
	Expect(err).NotTo(HaveOccurred())
	Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
	Expect(sess).To(gbytes.Say("90"))
}
<|endoftext|>


<commit_before>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package bundle

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/circonus-labs/circonus-agent/internal/config"
	"github.com/circonus-labs/circonus-agent/internal/config/defaults"
	"github.com/circonus-labs/circonus-agent/internal/release"
	"github.com/circonus-labs/go-apiclient"
	apiconf "github.com/circonus-labs/go-apiclient/config"
	"github.com/pkg/errors"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"
)

// Bundle exposes the check bundle management interface
type Bundle struct {
	statusActiveMetric    string
	statusActiveBroker    string
	brokerMaxResponseTime time.Duration
	brokerMaxRetries      int
	bundle                *apiclient.CheckBundle
	client                API
	lastRefresh           time.Time
	logger                zerolog.Logger
	manage                bool
	metricStates          *metricStates
	metricStateUpdate     bool
	refreshTTL            time.Duration
	stateFile             string
	statePath             string
	sync.Mutex
}

var ErrUninitialized = errors.New("uninitialized check bundle")

type ErrNotActive struct {
	Err      string
	Checks   string
	BundleID string
	Status   string
}

func (e *ErrNotActive) Error() string {
	if e == nil {
		return "<nil>"
	}
	s := e.Err
	if e.BundleID != "" {
		s = s + "Bundle: " + e.BundleID + " "
	}
	if e.Checks != "" {
		s = s + "Check(s): " + e.Checks + " "
	}
	if e.Status != "" {
		s = s + "(" + e.Status + ")"
	}
	return s
}

const (
	StatusActive = "active"
)

func New(client API) (*Bundle, error) {

	if client == nil {
		return nil, errors.New("invalid client (nil)")
	}

	cb := Bundle{
		client:                client,
		brokerMaxResponseTime: 500 * time.Millisecond,
		brokerMaxRetries:      5,
		bundle:                nil,
		logger:                log.With().Str("pkg", "bundle").Logger(),
		manage:                false,
		metricStateUpdate:     false,
		refreshTTL:            time.Duration(0),
		statePath:             viper.GetString(config.KeyCheckMetricStateDir),
		statusActiveBroker:    StatusActive,
		statusActiveMetric:    StatusActive,
	}

	isCreate := viper.GetBool(config.KeyCheckCreate)
	isManaged := viper.GetBool(config.KeyCheckEnableNewMetrics)
	isReverse := viper.GetBool(config.KeyReverse)
	cid := viper.GetString(config.KeyCheckBundleID)
	needCheck := false

	if isReverse || isManaged || (isCreate && cid == "") {
		needCheck = true
	}

	if !needCheck {
		cb.logger.Info().Msg("check management disabled")
		return &cb, nil // if we don't need a check, return a NOP object
	}

	// initialize the check bundle
	if err := cb.initCheckBundle(cid, isCreate); err != nil {
		return nil, errors.Wrap(err, "initializing check bundle")
	}

	// ensure a) the global check bundle id is set and b) it is set correctly
	// to the check bundle actually being used - need to do this even if the
	// check was created initially since user 'nobody' cannot create or update
	// the configuration (if one was used)
	viper.Set(config.KeyCheckBundleID, cb.bundle.CID)
	cb.logger.Debug().Interface("config", cb.bundle).Msg("using check bundle config")

	if isManaged && len(cb.bundle.MetricFilters) > 0 {
		cb.logger.Debug().Msg("disabling metric management, check bundle using metric_filters")
		isManaged = false
	}
	if !isManaged {
		cb.manage = false
		return &cb, nil
	}

	{
		//
		// NOTE: for metric management only
		//       metric management is DEPRECATED - this is for backwards compatibility
		//       and will be removed at some point in the future. all checks will be
		//       using metric filters going forward.
		//
		cb.stateFile = filepath.Join(cb.statePath, "metrics.json")

		if ok, err := cb.verifyStatePath(); !ok {
			if err != nil {
				cb.logger.Error().Err(err).Msg("verify state path")
			}
			cb.logger.Warn().Str("state_path", cb.statePath).Msg("encountered state path issue(s), disabling check-enable-new-metrics")
			viper.Set(config.KeyCheckEnableNewMetrics, false)
			cb.manage = false
			return &cb, nil
		}

		if ms, err := cb.loadState(); err != nil {
			cb.logger.Error().Err(err).Msg("unable to load existing metric states, all metrics considered existing")
		} else {
			cb.metricStates = ms
			cb.logger.Debug().Interface("metric_states", len(*cb.metricStates)).Msg("loaded metric states")
		}

		if err := cb.setMetricStates(&cb.bundle.Metrics); err != nil {
			return nil, errors.Wrap(err, "setting metric states")
		}
		cb.bundle.Metrics = []apiclient.CheckBundleMetric{} // save a little memory (or a lot depending on how many metrics are being managed...)
	}

	// check metrics refresh ttl
	ttl, err := time.ParseDuration(viper.GetString(config.KeyCheckMetricRefreshTTL))
	if err != nil {
		return nil, errors.Wrap(err, "parsing check metric refresh TTL")
	}
	if ttl == time.Duration(0) {
		ttl, err = time.ParseDuration(defaults.CheckMetricRefreshTTL)
		if err != nil {
			return nil, errors.Wrap(err, "parsing default check metric refresh TTL")
		}
	}

	cb.refreshTTL = ttl
	cb.manage = isManaged

	return &cb, nil
}

// CID returns the check bundle cid
func (cb *Bundle) CID() (string, error) {
	cb.Lock()
	defer cb.Unlock()

	if cb.bundle != nil {
		return cb.bundle.CID, nil
	}

	return "", ErrUninitialized
}

// Period returns check bundle period (intetrval between when broker should make requests)
func (cb *Bundle) Period() (uint, error) {
	cb.Lock()
	defer cb.Unlock()

	if cb.bundle != nil {
		return cb.bundle.Period, nil
	}

	return 0, ErrUninitialized
}

// Refresh re-loads the check bundle using the API (sets metric states if check bundle is managed)
func (cb *Bundle) Refresh() error {
	cb.Lock()
	defer cb.Unlock()

	if cb.bundle == nil {
		return ErrUninitialized
	}

	cb.logger.Debug().Msg("refreshing check configuration using API")

	b, err := cb.fetchCheckBundle(viper.GetString(config.KeyCheckBundleID))
	if err != nil {
		return errors.Wrap(err, "refresh check, fetching check")
	}

	cb.bundle = b

	if cb.manage {
		cb.logger.Debug().Msg("setting metric states")
		err := cb.setMetricStates(&cb.bundle.Metrics)
		if err != nil {
			return errors.Wrap(err, "setting metric states")
		}
	}
	cb.bundle.Metrics = []apiclient.CheckBundleMetric{} // save a little memory (or a lot depending on how many metrics are being managed...)

	return nil
}

// CheckCID returns the check cid at the passed index within the check bundle's checks array or an error if bundle not initialized
func (cb *Bundle) CheckCID(idx uint) (string, error) {
	cb.Lock()
	defer cb.Unlock()

	if cb.bundle == nil {
		return "", ErrUninitialized
	}
	if len(cb.bundle.Checks) == 0 {
		return "", errors.New("no checks found in check bundle")
	}
	if int(idx) > len(cb.bundle.Checks) {
		return "", errors.Errorf("invalid check index (%d>%d)", idx, len(cb.bundle.Checks))
	}

	return cb.bundle.Checks[idx], nil
}

// initCheck initializes the check for the agent.
// 1. fetch a check explicitly provided via CID
// 2. search for a check matching the current system
// 3. create a check for the system if --check-create specified
// if fetched, found, or created - set Check.bundle
// otherwise, return an error
func (cb *Bundle) initCheckBundle(cid string, create bool) error {
	var bundle *apiclient.CheckBundle

	// if explicit cid configured, attempt to fetch check bundle using cid
	if cid != "" {
		b, err := cb.fetchCheckBundle(cid)
		if err != nil {
			return errors.Wrapf(err, "fetching check for cid %s", cid)
		}
		bundle = b
	} else {
		// if no cid configured, attempt to find check bundle matching this system
		b, found, err := cb.findCheckBundle()
		if err != nil {
			if !create || found != 0 {
				return errors.Wrap(err, "unable to find a check for this system")
			}
			cb.logger.Info().Msg("no existing check found, creating")
			// attempt to create if not found and create flag ON
			b, err = cb.createCheckBundle()
			if err != nil {
				return errors.Wrap(err, "creating new check for this system")
			}
		}
		bundle = b
	}

	if bundle == nil {
		return errors.New("invalid Check object state, bundle is nil")
	}

	if bundle.Status != StatusActive {
		return &ErrNotActive{
			Err:      "not active",
			BundleID: bundle.CID,
			Checks:   strings.Join(bundle.Checks, ", "),
			Status:   bundle.Status,
		}
	}

	cb.bundle = bundle

	return nil
}

func (cb *Bundle) fetchCheckBundle(cid string) (*apiclient.CheckBundle, error) {
	if cid == "" {
		return nil, errors.New("invalid cid (empty)")
	}

	if ok, _ := regexp.MatchString(`^[0-9]+$`, cid); ok {
		cid = "/check_bundle/" + cid
	}

	if ok, _ := regexp.MatchString(`^/check_bundle/[0-9]+$`, cid); !ok {
		return nil, errors.Errorf("invalid cid (%s)", cid)
	}

	bundle, err := cb.client.FetchCheckBundle(apiclient.CIDType(&cid))
	if err != nil {
		return nil, errors.Wrapf(err, "unable to retrieve check bundle (%s)", cid)
	}

	if bundle.Status != StatusActive {
		return nil, &ErrNotActive{
			Err:      "not active",
			BundleID: bundle.CID,
			Checks:   strings.Join(bundle.Checks, ", "),
			Status:   bundle.Status,
		}
	}

	return bundle, nil
}

func (cb *Bundle) findCheckBundle() (*apiclient.CheckBundle, int, error) {
	target := viper.GetString(config.KeyCheckTarget)
	if target == "" {
		return nil, -1, errors.New("invalid check bundle target (empty)")
	}

	criteria := apiclient.SearchQueryType(fmt.Sprintf(`(active:1)(type:"json:nad")(target:"%s")`, target))
	bundles, err := cb.client.SearchCheckBundles(&criteria, nil)
	if err != nil {
		return nil, -1, errors.Wrap(err, "searching for check bundle")
	}

	found := len(*bundles)

	if found == 0 {
		return nil, found, errors.Errorf("no check bundles matched criteria (%s)", string(criteria))
	}

	if found > 1 {
		return nil, found, errors.Errorf("more than one (%d) check bundle matched criteria (%s)", len(*bundles), string(criteria))
	}

	return &(*bundles)[0], found, nil
}

func (cb *Bundle) createCheckBundle() (*apiclient.CheckBundle, error) {

	// parse the first listen address to use as the required
	// URL in the check config
	var targetAddr string
	{
		serverList := viper.GetStringSlice(config.KeyListen)
		if len(serverList) == 0 {
			serverList = []string{defaults.Listen}
		}
		if serverList[0][0:1] == ":" {
			serverList[0] = "localhost" + serverList[0]
		}
		ta, err := config.ParseListen(serverList[0])
		if err != nil {
			cb.logger.Error().Err(err).Str("addr", serverList[0]).Msg("resolving address")
			return nil, errors.Wrap(err, "parsing listen address")
		}
		targetAddr = ta.String()
	}

	target := viper.GetString(config.KeyCheckTarget)
	if target == "" {
		return nil, errors.New("invalid check bundle target (empty)")
	}

	cfg := apiclient.NewCheckBundle()
	cfg.Target = target
	cfg.DisplayName = viper.GetString(config.KeyCheckTitle)
	if cfg.DisplayName == "" {
		cfg.DisplayName = cfg.Target + " /agent"
	}
	note := fmt.Sprintf("created by %s %s", release.NAME, release.VERSION)
	cfg.Notes = &note
	cfg.Type = "json:nad"
	cfg.Config = apiclient.CheckBundleConfig{apiconf.URL: "http://" + targetAddr + "/"}

	cfg.Metrics = []apiclient.CheckBundleMetric{}
	cfg.MetricFilters = defaults.CheckMetricFilters
	if viper.GetString(config.KeyCheckMetricFilters) != "" {
		var filters [][]string
		if err := json.Unmarshal([]byte(viper.GetString(config.KeyCheckMetricFilters)), &filters); err != nil {
			return nil, errors.Wrap(err, "parsing check bundle metric filters")
		}
		cfg.MetricFilters = filters
	}

	tags := viper.GetString(config.KeyCheckTags)
	if tags != "" {
		cfg.Tags = strings.Split(tags, ",")
	}

	brokerCID := viper.GetString(config.KeyCheckBroker)
	if brokerCID == "" || strings.ToLower(brokerCID) == "select" {
		brokerList, err := cb.client.FetchBrokers()
		if err != nil {
			return nil, errors.Wrap(err, "select broker")
		}

		broker, err := cb.selectBroker("json:nad", brokerList)
		if err != nil {
			return nil, errors.Wrap(err, "selecting broker to create check bundle")
		}

		brokerCID = broker.CID
	}

	if ok, _ := regexp.MatchString(`^[0-9]+$`, brokerCID); ok {
		brokerCID = "/broker/" + brokerCID
	}

	cfg.Brokers = []string{brokerCID}

	bundle, err := cb.client.CreateCheckBundle(cfg)
	if err != nil {
		return nil, errors.Wrap(err, "creating check bundle")
	}

	return bundle, nil
}
<commit_msg>upd: multiple bundles, attempt to identify one created by agent (vs nad)<commit_after>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package bundle

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/circonus-labs/circonus-agent/internal/config"
	"github.com/circonus-labs/circonus-agent/internal/config/defaults"
	"github.com/circonus-labs/circonus-agent/internal/release"
	"github.com/circonus-labs/go-apiclient"
	apiconf "github.com/circonus-labs/go-apiclient/config"
	"github.com/pkg/errors"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"
)

// Bundle exposes the check bundle management interface
type Bundle struct {
	statusActiveMetric    string
	statusActiveBroker    string
	brokerMaxResponseTime time.Duration
	brokerMaxRetries      int
	bundle                *apiclient.CheckBundle
	client                API
	lastRefresh           time.Time
	logger                zerolog.Logger
	manage                bool
	metricStates          *metricStates
	metricStateUpdate     bool
	refreshTTL            time.Duration
	stateFile             string
	statePath             string
	sync.Mutex
}

var ErrUninitialized = errors.New("uninitialized check bundle")

type ErrNotActive struct {
	Err      string
	Checks   string
	BundleID string
	Status   string
}

func (e *ErrNotActive) Error() string {
	if e == nil {
		return "<nil>"
	}
	s := e.Err
	if e.BundleID != "" {
		s = s + "Bundle: " + e.BundleID + " "
	}
	if e.Checks != "" {
		s = s + "Check(s): " + e.Checks + " "
	}
	if e.Status != "" {
		s = s + "(" + e.Status + ")"
	}
	return s
}

const (
	StatusActive = "active"
)

func New(client API) (*Bundle, error) {

	if client == nil {
		return nil, errors.New("invalid client (nil)")
	}

	cb := Bundle{
		client:                client,
		brokerMaxResponseTime: 500 * time.Millisecond,
		brokerMaxRetries:      5,
		bundle:                nil,
		logger:                log.With().Str("pkg", "bundle").Logger(),
		manage:                false,
		metricStateUpdate:     false,
		refreshTTL:            time.Duration(0),
		statePath:             viper.GetString(config.KeyCheckMetricStateDir),
		statusActiveBroker:    StatusActive,
		statusActiveMetric:    StatusActive,
	}

	isCreate := viper.GetBool(config.KeyCheckCreate)
	isManaged := viper.GetBool(config.KeyCheckEnableNewMetrics)
	isReverse := viper.GetBool(config.KeyReverse)
	cid := viper.GetString(config.KeyCheckBundleID)
	needCheck := false

	if isReverse || isManaged || (isCreate && cid == "") {
		needCheck = true
	}

	if !needCheck {
		cb.logger.Info().Msg("check management disabled")
		return &cb, nil // if we don't need a check, return a NOP object
	}

	// initialize the check bundle
	if err := cb.initCheckBundle(cid, isCreate); err != nil {
		return nil, errors.Wrap(err, "initializing check bundle")
	}

	// ensure a) the global check bundle id is set and b) it is set correctly
	// to the check bundle actually being used - need to do this even if the
	// check was created initially since user 'nobody' cannot create or update
	// the configuration (if one was used)
	viper.Set(config.KeyCheckBundleID, cb.bundle.CID)
	cb.logger.Debug().Interface("config", cb.bundle).Msg("using check bundle config")

	if isManaged && len(cb.bundle.MetricFilters) > 0 {
		cb.logger.Debug().Msg("disabling metric management, check bundle using metric_filters")
		isManaged = false
	}
	if !isManaged {
		cb.manage = false
		return &cb, nil
	}

	{
		//
		// NOTE: for metric management only
		//       metric management is DEPRECATED - this is for backwards compatibility
		//       and will be removed at some point in the future. all checks will be
		//       using metric filters going forward.
		//
		cb.stateFile = filepath.Join(cb.statePath, "metrics.json")

		if ok, err := cb.verifyStatePath(); !ok {
			if err != nil {
				cb.logger.Error().Err(err).Msg("verify state path")
			}
			cb.logger.Warn().Str("state_path", cb.statePath).Msg("encountered state path issue(s), disabling check-enable-new-metrics")
			viper.Set(config.KeyCheckEnableNewMetrics, false)
			cb.manage = false
			return &cb, nil
		}

		if ms, err := cb.loadState(); err != nil {
			cb.logger.Error().Err(err).Msg("unable to load existing metric states, all metrics considered existing")
		} else {
			cb.metricStates = ms
			cb.logger.Debug().Interface("metric_states", len(*cb.metricStates)).Msg("loaded metric states")
		}

		if err := cb.setMetricStates(&cb.bundle.Metrics); err != nil {
			return nil, errors.Wrap(err, "setting metric states")
		}
		cb.bundle.Metrics = []apiclient.CheckBundleMetric{} // save a little memory (or a lot depending on how many metrics are being managed...)
	}

	// check metrics refresh ttl
	ttl, err := time.ParseDuration(viper.GetString(config.KeyCheckMetricRefreshTTL))
	if err != nil {
		return nil, errors.Wrap(err, "parsing check metric refresh TTL")
	}
	if ttl == time.Duration(0) {
		ttl, err = time.ParseDuration(defaults.CheckMetricRefreshTTL)
		if err != nil {
			return nil, errors.Wrap(err, "parsing default check metric refresh TTL")
		}
	}

	cb.refreshTTL = ttl
	cb.manage = isManaged

	return &cb, nil
}

// CID returns the check bundle cid
func (cb *Bundle) CID() (string, error) {
	cb.Lock()
	defer cb.Unlock()

	if cb.bundle != nil {
		return cb.bundle.CID, nil
	}

	return "", ErrUninitialized
}

// Period returns check bundle period (intetrval between when broker should make requests)
func (cb *Bundle) Period() (uint, error) {
	cb.Lock()
	defer cb.Unlock()

	if cb.bundle != nil {
		return cb.bundle.Period, nil
	}

	return 0, ErrUninitialized
}

// Refresh re-loads the check bundle using the API (sets metric states if check bundle is managed)
func (cb *Bundle) Refresh() error {
	cb.Lock()
	defer cb.Unlock()

	if cb.bundle == nil {
		return ErrUninitialized
	}

	cb.logger.Debug().Msg("refreshing check configuration using API")

	b, err := cb.fetchCheckBundle(viper.GetString(config.KeyCheckBundleID))
	if err != nil {
		return errors.Wrap(err, "refresh check, fetching check")
	}

	cb.bundle = b

	if cb.manage {
		cb.logger.Debug().Msg("setting metric states")
		err := cb.setMetricStates(&cb.bundle.Metrics)
		if err != nil {
			return errors.Wrap(err, "setting metric states")
		}
	}
	cb.bundle.Metrics = []apiclient.CheckBundleMetric{} // save a little memory (or a lot depending on how many metrics are being managed...)

	return nil
}

// CheckCID returns the check cid at the passed index within the check bundle's checks array or an error if bundle not initialized
func (cb *Bundle) CheckCID(idx uint) (string, error) {
	cb.Lock()
	defer cb.Unlock()

	if cb.bundle == nil {
		return "", ErrUninitialized
	}
	if len(cb.bundle.Checks) == 0 {
		return "", errors.New("no checks found in check bundle")
	}
	if int(idx) > len(cb.bundle.Checks) {
		return "", errors.Errorf("invalid check index (%d>%d)", idx, len(cb.bundle.Checks))
	}

	return cb.bundle.Checks[idx], nil
}

// initCheck initializes the check for the agent.
// 1. fetch a check explicitly provided via CID
// 2. search for a check matching the current system
// 3. create a check for the system if --check-create specified
// if fetched, found, or created - set Check.bundle
// otherwise, return an error
func (cb *Bundle) initCheckBundle(cid string, create bool) error {
	var bundle *apiclient.CheckBundle

	// if explicit cid configured, attempt to fetch check bundle using cid
	if cid != "" {
		b, err := cb.fetchCheckBundle(cid)
		if err != nil {
			return errors.Wrapf(err, "fetching check for cid %s", cid)
		}
		bundle = b
	} else {
		// if no cid configured, attempt to find check bundle matching this system
		b, found, err := cb.findCheckBundle()
		if err != nil {
			if !create || found != 0 {
				return errors.Wrap(err, "unable to find a check for this system")
			}
			cb.logger.Info().Msg("no existing check found, creating")
			// attempt to create if not found and create flag ON
			b, err = cb.createCheckBundle()
			if err != nil {
				return errors.Wrap(err, "creating new check for this system")
			}
		}
		bundle = b
	}

	if bundle == nil {
		return errors.New("invalid Check object state, bundle is nil")
	}

	if bundle.Status != StatusActive {
		return &ErrNotActive{
			Err:      "not active",
			BundleID: bundle.CID,
			Checks:   strings.Join(bundle.Checks, ", "),
			Status:   bundle.Status,
		}
	}

	cb.bundle = bundle

	return nil
}

func (cb *Bundle) fetchCheckBundle(cid string) (*apiclient.CheckBundle, error) {
	if cid == "" {
		return nil, errors.New("invalid cid (empty)")
	}

	if ok, _ := regexp.MatchString(`^[0-9]+$`, cid); ok {
		cid = "/check_bundle/" + cid
	}

	if ok, _ := regexp.MatchString(`^/check_bundle/[0-9]+$`, cid); !ok {
		return nil, errors.Errorf("invalid cid (%s)", cid)
	}

	bundle, err := cb.client.FetchCheckBundle(apiclient.CIDType(&cid))
	if err != nil {
		return nil, errors.Wrapf(err, "unable to retrieve check bundle (%s)", cid)
	}

	if bundle.Status != StatusActive {
		return nil, &ErrNotActive{
			Err:      "not active",
			BundleID: bundle.CID,
			Checks:   strings.Join(bundle.Checks, ", "),
			Status:   bundle.Status,
		}
	}

	return bundle, nil
}

func (cb *Bundle) findCheckBundle() (*apiclient.CheckBundle, int, error) {
	target := viper.GetString(config.KeyCheckTarget)
	if target == "" {
		return nil, -1, errors.New("invalid check bundle target (empty)")
	}

	criteria := apiclient.SearchQueryType(fmt.Sprintf(`(active:1)(type:"json:nad")(target:"%s")`, target))
	bundles, err := cb.client.SearchCheckBundles(&criteria, nil)
	if err != nil {
		return nil, -1, errors.Wrap(err, "searching for check bundle")
	}

	found := len(*bundles)

	if found == 0 {
		return nil, found, errors.Errorf("no check bundles matched criteria (%s)", string(criteria))
	}

	if found > 1 {
		// if more than one bundle, find one created by the circonus-agent
		// if multiple bundles created by agent, error, otherwise return the one from the agent
		matched := 0
		idx := -1
		for i, b := range *bundles {
			if b.Notes != nil && strings.Contains(*b.Notes, release.NAME) {
				idx = i
				matched++
			}
		}
		if matched == 1 {
			cb.logger.Warn().Int("found", found).Str("bundle", (*bundles)[idx].CID).Msgf("multiple checks found, using one created by %s", release.NAME)
			return &(*bundles)[idx], matched, nil
		}
		return nil, found, errors.Errorf("more than one (%d) check bundle matched criteria (%s)", found, string(criteria))
	}

	return &(*bundles)[0], found, nil
}

func (cb *Bundle) createCheckBundle() (*apiclient.CheckBundle, error) {

	// parse the first listen address to use as the required
	// URL in the check config
	var targetAddr string
	{
		serverList := viper.GetStringSlice(config.KeyListen)
		if len(serverList) == 0 {
			serverList = []string{defaults.Listen}
		}
		if serverList[0][0:1] == ":" {
			serverList[0] = "localhost" + serverList[0]
		}
		ta, err := config.ParseListen(serverList[0])
		if err != nil {
			cb.logger.Error().Err(err).Str("addr", serverList[0]).Msg("resolving address")
			return nil, errors.Wrap(err, "parsing listen address")
		}
		targetAddr = ta.String()
	}

	target := viper.GetString(config.KeyCheckTarget)
	if target == "" {
		return nil, errors.New("invalid check bundle target (empty)")
	}

	cfg := apiclient.NewCheckBundle()
	cfg.Target = target
	cfg.DisplayName = viper.GetString(config.KeyCheckTitle)
	if cfg.DisplayName == "" {
		cfg.DisplayName = cfg.Target + " /agent"
	}
	note := fmt.Sprintf("created by %s %s", release.NAME, release.VERSION)
	cfg.Notes = &note
	cfg.Type = "json:nad"
	cfg.Config = apiclient.CheckBundleConfig{apiconf.URL: "http://" + targetAddr + "/"}

	cfg.Metrics = []apiclient.CheckBundleMetric{}
	cfg.MetricFilters = defaults.CheckMetricFilters
	if viper.GetString(config.KeyCheckMetricFilters) != "" {
		var filters [][]string
		if err := json.Unmarshal([]byte(viper.GetString(config.KeyCheckMetricFilters)), &filters); err != nil {
			return nil, errors.Wrap(err, "parsing check bundle metric filters")
		}
		cfg.MetricFilters = filters
	}

	tags := viper.GetString(config.KeyCheckTags)
	if tags != "" {
		cfg.Tags = strings.Split(tags, ",")
	}

	brokerCID := viper.GetString(config.KeyCheckBroker)
	if brokerCID == "" || strings.ToLower(brokerCID) == "select" {
		brokerList, err := cb.client.FetchBrokers()
		if err != nil {
			return nil, errors.Wrap(err, "select broker")
		}

		broker, err := cb.selectBroker("json:nad", brokerList)
		if err != nil {
			return nil, errors.Wrap(err, "selecting broker to create check bundle")
		}

		brokerCID = broker.CID
	}

	if ok, _ := regexp.MatchString(`^[0-9]+$`, brokerCID); ok {
		brokerCID = "/broker/" + brokerCID
	}

	cfg.Brokers = []string{brokerCID}

	bundle, err := cb.client.CreateCheckBundle(cfg)
	if err != nil {
		return nil, errors.Wrap(err, "creating check bundle")
	}

	return bundle, nil
}
<|endoftext|>


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

package lifecycle

import (
	"fmt"
	"github.com/GoogleCloudPlatform/serverless-sample-tester/internal/util"
	"os"
	"os/exec"
)

// Lifecycle is a list of ordered exec.Cmd that should be run to execute a certain process.
type Lifecycle []*exec.Cmd

// Execute executes the commands of a lifecycle in the provided directory.
func (l Lifecycle) Execute(commandsDir string) error {
	for _, c := range l {
		_, err := util.ExecCommand(c, commandsDir)
		if err != nil {
			return fmt.Errorf("[Lifecycle.Execute] executing lifecycle command: %w", err)
		}
	}

	return nil
}

// NewLifecycle returns a lifecycle built with reasonable defaults based on whether the sample is java-based
// (has a pom.xml) that doesn't have a Dockerfile or isn't.
func NewLifecycle(sampleDir, serviceName, gcrURL string) Lifecycle {
	pomPath := fmt.Sprintf("%s/pom.xml", sampleDir)
	dockerfilePath := fmt.Sprintf("%s/Dockerfile", sampleDir)

	_, err := os.Stat(pomPath)
	pomExists := err == nil

	_, err = os.Stat(dockerfilePath)
	dockerfileExists := err == nil

	if pomExists && !dockerfileExists {
		return buildDefaultJavaLifecycle(serviceName, gcrURL)
	}

	return buildDefaultLifecycle(serviceName, gcrURL)
}

// buildDefaultLifecycle builds a build and deploy command lifecycle with reasonable defaults for a non-Java
// project. It uses `gcloud builds submit` for building the samples container image and submitting it to the container
// and `gcloud run deploy` for deploying it to Cloud Run.
func buildDefaultLifecycle(serviceName, gcrURL string) Lifecycle {
	a0 := append(util.GcloudCommonFlags, "builds", "submit", fmt.Sprintf("--tag=%s", gcrURL))
	a1 := append(util.GcloudCommonFlags, "run", "deploy", serviceName, fmt.Sprintf("--image=%s", gcrURL),
		"--platform=managed")

	return Lifecycle{
		exec.Command("gcloud", a0...),
		exec.Command("gcloud", a1...),
	}
}

// buildDefaultJavaLifecycle builds a build and deploy command lifecycle with reasonable defaults for Java
// samples. It uses `com.google.cloud.tools:jib-maven-plugin:2.0.0:build` for building the samples container image and
// submitting it to the container and `gcloud run deploy` for deploying it to Cloud Run.
func buildDefaultJavaLifecycle(serviceName, gcrURL string) Lifecycle {
	l := buildDefaultLifecycle(serviceName, gcrURL)

	l[0] = exec.Command("mvn",
		"compile",
		"com.google.cloud.tools:jib-maven-plugin:2.0.0:build",
		fmt.Sprintf("-Dimage=%s", gcrURL),
	)

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

package lifecycle

import (
	"fmt"
	"github.com/GoogleCloudPlatform/serverless-sample-tester/internal/util"
	"os"
	"os/exec"
)

// Lifecycle is a list of ordered exec.Cmd that should be run to execute a certain process.
type Lifecycle []*exec.Cmd

// Execute executes the commands of a lifecycle in the provided directory.
func (l Lifecycle) Execute(commandsDir string) error {
	for _, c := range l {
		if c == nil {
			continue
		}

		_, err := util.ExecCommand(c, commandsDir)
		if err != nil {
			return fmt.Errorf("[Lifecycle.Execute] executing lifecycle command: %w", err)
		}
	}

	return nil
}

// NewLifecycle returns a lifecycle built with reasonable defaults based on whether the sample is java-based
// (has a pom.xml) that doesn't have a Dockerfile or isn't.
func NewLifecycle(sampleDir, serviceName, gcrURL string) Lifecycle {
	pomPath := fmt.Sprintf("%s/pom.xml", sampleDir)
	dockerfilePath := fmt.Sprintf("%s/Dockerfile", sampleDir)

	_, err := os.Stat(pomPath)
	pomExists := err == nil

	_, err = os.Stat(dockerfilePath)
	dockerfileExists := err == nil

	if pomExists && !dockerfileExists {
		return buildDefaultJavaLifecycle(serviceName, gcrURL)
	}

	return buildDefaultLifecycle(serviceName, gcrURL)
}

// buildDefaultLifecycle builds a build and deploy command lifecycle with reasonable defaults for a non-Java
// project. It uses `gcloud builds submit` for building the samples container image and submitting it to the container
// and `gcloud run deploy` for deploying it to Cloud Run.
func buildDefaultLifecycle(serviceName, gcrURL string) Lifecycle {
	a0 := append(util.GcloudCommonFlags, "builds", "submit", fmt.Sprintf("--tag=%s", gcrURL))
	a1 := append(util.GcloudCommonFlags, "run", "deploy", serviceName, fmt.Sprintf("--image=%s", gcrURL),
		"--platform=managed")

	return Lifecycle{
		exec.Command("gcloud", a0...),
		exec.Command("gcloud", a1...),
	}
}

// buildDefaultJavaLifecycle builds a build and deploy command lifecycle with reasonable defaults for Java
// samples. It uses `com.google.cloud.tools:jib-maven-plugin:2.0.0:build` for building the samples container image and
// submitting it to the container and `gcloud run deploy` for deploying it to Cloud Run.
func buildDefaultJavaLifecycle(serviceName, gcrURL string) Lifecycle {
	l := buildDefaultLifecycle(serviceName, gcrURL)

	l[0] = exec.Command("mvn",
		"compile",
		"com.google.cloud.tools:jib-maven-plugin:2.0.0:build",
		fmt.Sprintf("-Dimage=%s", gcrURL),
	)

	return l
}
<|endoftext|>


<commit_before>package jira

import (
	// Stdlib
	"log"

	// Vendor
	"github.com/salsita/go-jira/v2/jira"
)

const (
	iconOpen   = "https://raw.githubusercontent.com/salsaflow/salsaflow-daemon/develop/internal/trackers/jira/img/blacktocat-16.png"
	iconClosed = "https://raw.githubusercontent.com/salsaflow/salsaflow-daemon/develop/internal/trackers/jira/img/greentocat-16.png"
)

type commonStory struct {
	client *jira.Client
	issue  *jira.Issue
}

// common.Story interface implementation ---------------------------------------

func (s *commonStory) OnReviewRequestOpened(rrID, rrURL string) error {
	// Prepare the remote link object.
	var link jira.RemoteIssueLink
	link.GlobalId = rrURL
	link.Object.Title = toTitle(rrID)
	link.Object.URL = rrURL
	link.Object.Icon.URL = iconOpen

	// Create the remote link.
	return s.createRemoteLink(&link)
}

func (s *commonStory) OnReviewRequestClosed(rrID, rrURL string) error {
	// List the remote link associated with this issue.
	links, err := s.listRemoteLinks()
	if err != nil {
		return nil
	}

	var (
		title       = toTitle(rrID)
		linkFound   = false
		allResolved = true
	)
	for _, link := range links {
		if link.Object.Title == title {
			linkFound = true
			continue
		}
		if !link.Object.Status.Resolved {
			allResolved = false
		}
	}

	// In case the link is there, and it should be, mark it as resolved.
	if linkFound {
		if err := s.setRemoteLinkResolved(rrID, rrURL, true); err != nil {
			return err
		}
	} else {
		log.Printf("JIRA: remote link not found: issue %v, review issue %v\n", s.issue.Key, rrURL)
	}

	// In case all links are resolved, mark the issue as reviewed.
	if allResolved {
		return s.markIssueAsReviewed()
	}

	return nil
}

func (s *commonStory) OnReviewRequestReopened(rrID, rrURL string) error {
	// Make sure the remote link exists.
	link, err := s.findRemoteLink(rrID)
	if err != nil {
		return err
	}
	if link == nil {
		log.Printf("JIRA: remote link not found: issue %v, review issue %v\n", s.issue.Key, rrURL)
		return nil
	}
	// No need to do anything really, everything is in OnReviewRequestClosed.

	// Mark the link as resolved.
	return s.setRemoteLinkResolved(rrID, rrURL, false)
}

func (s *commonStory) MarkAsReviewed() error {
	// No need to do anything really, everything is in OnReviewRequestClosed.
	return nil
}

// Internal methods ------------------------------------------------------------

func (s *commonStory) setRemoteLinkResolved(rrID, rrURL string, resolved bool) error {
	// Prepare the update object.
	var update jira.RemoteIssueLink
	update.GlobalId = rrURL
	update.Object.Title = toTitle(rrID)
	update.Object.URL = rrURL
	update.Object.Status.Resolved = resolved

	var icon string
	if resolved {
		icon = iconClosed
	} else {
		icon = iconOpen
	}
	update.Object.Icon.URL = icon

	// Update the remote link.
	return s.updateRemoteLink(&update)
}

func (s *commonStory) createRemoteLink(link *jira.RemoteIssueLink) error {
	_, err := s.client.RemoteIssueLinks.Create(s.issue.Key, link)
	return err
}

func (s *commonStory) listRemoteLinks() ([]*jira.RemoteIssueLink, error) {
	links, _, err := s.client.RemoteIssueLinks.List(s.issue.Key)
	return links, err
}

func (s *commonStory) updateRemoteLink(link *jira.RemoteIssueLink) error {
	_, err := s.client.RemoteIssueLinks.Update(s.issue.Key, link)
	return err
}

func (s *commonStory) findRemoteLink(rrID string) (*jira.RemoteIssueLink, error) {
	links, _, err := s.client.RemoteIssueLinks.List(s.issue.Key)
	if err != nil {
		return nil, err
	}

	title := toTitle(rrID)
	for _, link := range links {
		if link.Object.Title == title {
			return link, nil
		}
	}
	return nil, nil
}

func (s *commonStory) markIssueAsReviewed() error {
	switch s.issue.Fields.Status.Id {
	// In case the issue is still Being Implemented, we are done.
	case statusIdBeingImplemented:
		return nil

	// In case the issue is Implemented, we proceed with the transition.
	case statusIdImplemented:
		_, err := s.client.Issues.PerformTransition(s.issue.Key, jira.M{
			"transition": jira.M{
				"id": transitionIdMarkAsReviewed,
			},
		})
		return err

	// By default we log a warning and return.
	default:
		log.Printf(
			"JIRA: issue %v: not Implemented nor Being Implemented\n", s.issue.Key)
		return nil
	}
}

func toTitle(rrID string) string {
	return "Review issue #" + rrID
}
<commit_msg>trackers/jira: Insert more logging statements<commit_after>package jira

import (
	// Stdlib
	"log"

	// Vendor
	"github.com/salsita/go-jira/v2/jira"
)

const (
	iconOpen   = "https://raw.githubusercontent.com/salsaflow/salsaflow-daemon/develop/internal/trackers/jira/img/blacktocat-16.png"
	iconClosed = "https://raw.githubusercontent.com/salsaflow/salsaflow-daemon/develop/internal/trackers/jira/img/greentocat-16.png"
)

type commonStory struct {
	client *jira.Client
	issue  *jira.Issue
}

// common.Story interface implementation ---------------------------------------

func (s *commonStory) OnReviewRequestOpened(rrID, rrURL string) error {
	// Prepare the remote link object.
	var link jira.RemoteIssueLink
	link.GlobalId = rrURL
	link.Object.Title = toTitle(rrID)
	link.Object.URL = rrURL
	link.Object.Icon.URL = iconOpen

	// Create the remote link.
	return s.createRemoteLink(&link)
}

func (s *commonStory) OnReviewRequestClosed(rrID, rrURL string) error {
	// List the remote link associated with this issue.
	links, err := s.listRemoteLinks()
	if err != nil {
		return nil
	}

	var (
		title       = toTitle(rrID)
		linkFound   = false
		allResolved = true
	)
	for _, link := range links {
		if link.Object.Title == title {
			linkFound = true
			continue
		}
		if !link.Object.Status.Resolved {
			allResolved = false
		}
	}

	// In case the link is there, and it should be, mark it as resolved.
	if linkFound {
		if err := s.setRemoteLinkResolved(rrID, rrURL, true); err != nil {
			return err
		}
	} else {
		log.Printf("JIRA: remote link not found: issue %v, review issue %v\n", s.issue.Key, rrURL)
	}

	// In case all links are resolved, mark the issue as reviewed.
	if allResolved {
		return s.markIssueAsReviewed()
	}

	return nil
}

func (s *commonStory) OnReviewRequestReopened(rrID, rrURL string) error {
	// Make sure the remote link exists.
	link, err := s.findRemoteLink(rrID)
	if err != nil {
		return err
	}
	if link == nil {
		log.Printf("JIRA: remote link not found: issue %v, review issue %v\n", s.issue.Key, rrURL)
		return nil
	}
	// No need to do anything really, everything is in OnReviewRequestClosed.

	// Mark the link as resolved.
	return s.setRemoteLinkResolved(rrID, rrURL, false)
}

func (s *commonStory) MarkAsReviewed() error {
	// No need to do anything really, everything is in OnReviewRequestClosed.
	return nil
}

// Internal methods ------------------------------------------------------------

func (s *commonStory) setRemoteLinkResolved(rrID, rrURL string, resolved bool) error {
	log.Printf("JIRA: Setting remote link resolution to %v for %v\n", resolved, rrURL)

	// Prepare the update object.
	var update jira.RemoteIssueLink
	update.GlobalId = rrURL
	update.Object.Title = toTitle(rrID)
	update.Object.URL = rrURL
	update.Object.Status.Resolved = resolved

	var icon string
	if resolved {
		icon = iconClosed
	} else {
		icon = iconOpen
	}
	update.Object.Icon.URL = icon

	// Update the remote link.
	return s.updateRemoteLink(&update)
}

func (s *commonStory) createRemoteLink(link *jira.RemoteIssueLink) error {
	log.Printf("JIRA: Creating a remote link: %+v\n", *link)
	_, err := s.client.RemoteIssueLinks.Create(s.issue.Key, link)
	return err
}

func (s *commonStory) listRemoteLinks() ([]*jira.RemoteIssueLink, error) {
	links, _, err := s.client.RemoteIssueLinks.List(s.issue.Key)
	return links, err
}

func (s *commonStory) updateRemoteLink(link *jira.RemoteIssueLink) error {
	log.Printf("JIRA: Updating a remote link: %+v\n", *link)
	_, err := s.client.RemoteIssueLinks.Update(s.issue.Key, link)
	return err
}

func (s *commonStory) findRemoteLink(rrID string) (*jira.RemoteIssueLink, error) {
	links, _, err := s.client.RemoteIssueLinks.List(s.issue.Key)
	if err != nil {
		return nil, err
	}

	title := toTitle(rrID)
	for _, link := range links {
		if link.Object.Title == title {
			return link, nil
		}
	}
	return nil, nil
}

func (s *commonStory) markIssueAsReviewed() error {
	switch s.issue.Fields.Status.Id {
	// In case the issue is still Being Implemented, we are done.
	case statusIdBeingImplemented:
		return nil

	// In case the issue is Implemented, we proceed with the transition.
	case statusIdImplemented:
		log.Printf("JIRA: Marking issues %v as reviewed\n", s.issue.Key)
		_, err := s.client.Issues.PerformTransition(s.issue.Key, jira.M{
			"transition": jira.M{
				"id": transitionIdMarkAsReviewed,
			},
		})
		return err

	// By default we log a warning and return.
	default:
		log.Printf(
			"JIRA: issue %v: not Implemented nor Being Implemented\n", s.issue.Key)
		return nil
	}
}

func toTitle(rrID string) string {
	return "Review issue #" + rrID
}
<|endoftext|>


<commit_before>// Copyright 2017 The Go 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 base

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

func getwd() string {
	wd, err := os.Getwd()
	if err != nil {
		Fatalf("cannot determine current directory: %v", err)
	}
	return wd
}

var Cwd = getwd()

// ShortPath returns an absolute or relative name for path, whatever is shorter.
func ShortPath(path string) string {
	if rel, err := filepath.Rel(Cwd, path); err == nil && len(rel) < len(path) {
		return rel
	}
	return path
}

// RelPaths returns a copy of paths with absolute paths
// made relative to the current directory if they would be shorter.
func RelPaths(paths []string) []string {
	var out []string
	// TODO(rsc): Can this use Cwd from above?
	pwd, _ := os.Getwd()
	for _, p := range paths {
		rel, err := filepath.Rel(pwd, p)
		if err == nil && len(rel) < len(p) {
			p = rel
		}
		out = append(out, p)
	}
	return out
}

// IsTestFile reports whether the source file is a set of tests and should therefore
// be excluded from coverage analysis.
func IsTestFile(file string) bool {
	// We don't cover tests, only the code they test.
	return strings.HasSuffix(file, "_test.go")
}
<commit_msg>cmd/go: use the global rooted path name<commit_after>// Copyright 2017 The Go 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 base

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

func getwd() string {
	wd, err := os.Getwd()
	if err != nil {
		Fatalf("cannot determine current directory: %v", err)
	}
	return wd
}

var Cwd = getwd()

// ShortPath returns an absolute or relative name for path, whatever is shorter.
func ShortPath(path string) string {
	if rel, err := filepath.Rel(Cwd, path); err == nil && len(rel) < len(path) {
		return rel
	}
	return path
}

// RelPaths returns a copy of paths with absolute paths
// made relative to the current directory if they would be shorter.
func RelPaths(paths []string) []string {
	var out []string
	for _, p := range paths {
		rel, err := filepath.Rel(Cwd, p)
		if err == nil && len(rel) < len(p) {
			p = rel
		}
		out = append(out, p)
	}
	return out
}

// IsTestFile reports whether the source file is a set of tests and should therefore
// be excluded from coverage analysis.
func IsTestFile(file string) bool {
	// We don't cover tests, only the code they test.
	return strings.HasSuffix(file, "_test.go")
}
<|endoftext|>