<commit_before>package reference // COPY WITH EDITS FROM DOCKER/DOCKER

import (
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/docker/distribution/digest"
	distreference "github.com/docker/distribution/reference"
)

const (
	// DefaultTag defines the default tag used when performing images related actions and no tag or digest is specified
	DefaultTag = "latest"
	// DefaultHostname is the default built-in hostname
	DefaultHostname = "docker.io"
	// LegacyDefaultHostname is automatically converted to DefaultHostname
	LegacyDefaultHostname = "index.docker.io"
	// DefaultRepoPrefix is the prefix used for default repositories in default host
	DefaultRepoPrefix = "library/"
)

// Named is an object with a full name
type Named interface {
	// Name returns normalized repository name, like "ubuntu".
	Name() string
	// String returns full reference, like "ubuntu@sha256:abcdef..."
	String() string
	// FullName returns full repository name with hostname, like "docker.io/library/ubuntu"
	FullName() string
	// Hostname returns hostname for the reference, like "docker.io"
	Hostname() string
	// RemoteName returns the repository component of the full name, like "library/ubuntu"
	RemoteName() string
}

// NamedTagged is an object including a name and tag.
type NamedTagged interface {
	Named
	Tag() string
}

// Canonical reference is an object with a fully unique
// name including a name with hostname and digest
type Canonical interface {
	Named
	Digest() digest.Digest
}

// ParseNamed parses s and returns a syntactically valid reference implementing
// the Named interface. The reference must have a name, otherwise an error is
// returned.
// If an error was encountered it is returned, along with a nil Reference.
func ParseNamed(s string) (Named, error) {
	named, err := distreference.ParseNamed(s)
	if err != nil {
		return nil, fmt.Errorf("Error parsing reference: %q is not a valid repository/tag", s)
	}
	r, err := WithName(named.Name())
	if err != nil {
		return nil, err
	}
	if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
		return WithDigest(r, canonical.Digest())
	}
	if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
		return WithTag(r, tagged.Tag())
	}
	return r, nil
}

// WithName returns a named object representing the given string. If the input
// is invalid ErrReferenceInvalidFormat will be returned.
func WithName(name string) (Named, error) {
	name, err := normalize(name)
	if err != nil {
		return nil, err
	}
	if err := validateName(name); err != nil {
		return nil, err
	}
	r, err := distreference.WithName(name)
	if err != nil {
		return nil, err
	}
	return &namedRef{r}, nil
}

// WithTag combines the name from "name" and the tag from "tag" to form a
// reference incorporating both the name and the tag.
func WithTag(name Named, tag string) (NamedTagged, error) {
	r, err := distreference.WithTag(name, tag)
	if err != nil {
		return nil, err
	}
	return &taggedRef{namedRef{r}}, nil
}

// WithDigest combines the name from "name" and the digest from "digest" to form
// a reference incorporating both the name and the digest.
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
	r, err := distreference.WithDigest(name, digest)
	if err != nil {
		return nil, err
	}
	return &canonicalRef{namedRef{r}}, nil
}

type namedRef struct {
	distreference.Named
}
type taggedRef struct {
	namedRef
}
type canonicalRef struct {
	namedRef
}

func (r *namedRef) FullName() string {
	hostname, remoteName := splitHostname(r.Name())
	return hostname + "/" + remoteName
}
func (r *namedRef) Hostname() string {
	hostname, _ := splitHostname(r.Name())
	return hostname
}
func (r *namedRef) RemoteName() string {
	_, remoteName := splitHostname(r.Name())
	return remoteName
}
func (r *taggedRef) Tag() string {
	return r.namedRef.Named.(distreference.NamedTagged).Tag()
}
func (r *canonicalRef) Digest() digest.Digest {
	return r.namedRef.Named.(distreference.Canonical).Digest()
}

// WithDefaultTag adds a default tag to a reference if it only has a repo name.
func WithDefaultTag(ref Named) Named {
	if IsNameOnly(ref) {
		ref, _ = WithTag(ref, DefaultTag)
	}
	return ref
}

// IsNameOnly returns true if reference only contains a repo name.
func IsNameOnly(ref Named) bool {
	if _, ok := ref.(NamedTagged); ok {
		return false
	}
	if _, ok := ref.(Canonical); ok {
		return false
	}
	return true
}

// splitHostname splits a repository name to hostname and remotename string.
// If no valid hostname is found, the default hostname is used. Repository name
// needs to be already validated before.
func splitHostname(name string) (hostname, remoteName string) {
	i := strings.IndexRune(name, '/')
	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
		hostname, remoteName = DefaultHostname, name
	} else {
		hostname, remoteName = name[:i], name[i+1:]
	}
	if hostname == LegacyDefaultHostname {
		hostname = DefaultHostname
	}
	if hostname == DefaultHostname && !strings.ContainsRune(remoteName, '/') {
		remoteName = DefaultRepoPrefix + remoteName
	}
	return
}

// normalize returns a repository name in its normalized form, meaning it
// will not contain default hostname nor library/ prefix for official images.
func normalize(name string) (string, error) {
	host, remoteName := splitHostname(name)
	if strings.ToLower(remoteName) != remoteName {
		return "", errors.New("invalid reference format: repository name must be lowercase")
	}
	if host == DefaultHostname {
		if strings.HasPrefix(remoteName, DefaultRepoPrefix) {
			return strings.TrimPrefix(remoteName, DefaultRepoPrefix), nil
		}
		return remoteName, nil
	}
	return name, nil
}

// EDIT FROM DOCKER/DOCKER TO NOT IMPORT IMAGE.V1

func validateName(name string) error {
	if err := ValidateIDV1(name); err == nil {
		return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
	}
	return nil
}

var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)

// ValidateIDV1 checks whether an ID string is a valid image ID.
func ValidateIDV1(id string) error {
	if ok := validHex.MatchString(id); !ok {
		return fmt.Errorf("image ID %q is invalid", id)
	}
	return nil
}
<commit_msg>Do not drop the underlying error message when a Docker reference is invalid<commit_after>package reference // COPY WITH EDITS FROM DOCKER/DOCKER

import (
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/docker/distribution/digest"
	distreference "github.com/docker/distribution/reference"
)

const (
	// DefaultTag defines the default tag used when performing images related actions and no tag or digest is specified
	DefaultTag = "latest"
	// DefaultHostname is the default built-in hostname
	DefaultHostname = "docker.io"
	// LegacyDefaultHostname is automatically converted to DefaultHostname
	LegacyDefaultHostname = "index.docker.io"
	// DefaultRepoPrefix is the prefix used for default repositories in default host
	DefaultRepoPrefix = "library/"
)

// Named is an object with a full name
type Named interface {
	// Name returns normalized repository name, like "ubuntu".
	Name() string
	// String returns full reference, like "ubuntu@sha256:abcdef..."
	String() string
	// FullName returns full repository name with hostname, like "docker.io/library/ubuntu"
	FullName() string
	// Hostname returns hostname for the reference, like "docker.io"
	Hostname() string
	// RemoteName returns the repository component of the full name, like "library/ubuntu"
	RemoteName() string
}

// NamedTagged is an object including a name and tag.
type NamedTagged interface {
	Named
	Tag() string
}

// Canonical reference is an object with a fully unique
// name including a name with hostname and digest
type Canonical interface {
	Named
	Digest() digest.Digest
}

// ParseNamed parses s and returns a syntactically valid reference implementing
// the Named interface. The reference must have a name, otherwise an error is
// returned.
// If an error was encountered it is returned, along with a nil Reference.
func ParseNamed(s string) (Named, error) {
	named, err := distreference.ParseNamed(s)
	if err != nil {
		return nil, fmt.Errorf("Error parsing reference: %q is not a valid repository/tag: %s", s, err.Error())
	}
	r, err := WithName(named.Name())
	if err != nil {
		return nil, err
	}
	if canonical, isCanonical := named.(distreference.Canonical); isCanonical {
		return WithDigest(r, canonical.Digest())
	}
	if tagged, isTagged := named.(distreference.NamedTagged); isTagged {
		return WithTag(r, tagged.Tag())
	}
	return r, nil
}

// WithName returns a named object representing the given string. If the input
// is invalid ErrReferenceInvalidFormat will be returned.
func WithName(name string) (Named, error) {
	name, err := normalize(name)
	if err != nil {
		return nil, err
	}
	if err := validateName(name); err != nil {
		return nil, err
	}
	r, err := distreference.WithName(name)
	if err != nil {
		return nil, err
	}
	return &namedRef{r}, nil
}

// WithTag combines the name from "name" and the tag from "tag" to form a
// reference incorporating both the name and the tag.
func WithTag(name Named, tag string) (NamedTagged, error) {
	r, err := distreference.WithTag(name, tag)
	if err != nil {
		return nil, err
	}
	return &taggedRef{namedRef{r}}, nil
}

// WithDigest combines the name from "name" and the digest from "digest" to form
// a reference incorporating both the name and the digest.
func WithDigest(name Named, digest digest.Digest) (Canonical, error) {
	r, err := distreference.WithDigest(name, digest)
	if err != nil {
		return nil, err
	}
	return &canonicalRef{namedRef{r}}, nil
}

type namedRef struct {
	distreference.Named
}
type taggedRef struct {
	namedRef
}
type canonicalRef struct {
	namedRef
}

func (r *namedRef) FullName() string {
	hostname, remoteName := splitHostname(r.Name())
	return hostname + "/" + remoteName
}
func (r *namedRef) Hostname() string {
	hostname, _ := splitHostname(r.Name())
	return hostname
}
func (r *namedRef) RemoteName() string {
	_, remoteName := splitHostname(r.Name())
	return remoteName
}
func (r *taggedRef) Tag() string {
	return r.namedRef.Named.(distreference.NamedTagged).Tag()
}
func (r *canonicalRef) Digest() digest.Digest {
	return r.namedRef.Named.(distreference.Canonical).Digest()
}

// WithDefaultTag adds a default tag to a reference if it only has a repo name.
func WithDefaultTag(ref Named) Named {
	if IsNameOnly(ref) {
		ref, _ = WithTag(ref, DefaultTag)
	}
	return ref
}

// IsNameOnly returns true if reference only contains a repo name.
func IsNameOnly(ref Named) bool {
	if _, ok := ref.(NamedTagged); ok {
		return false
	}
	if _, ok := ref.(Canonical); ok {
		return false
	}
	return true
}

// splitHostname splits a repository name to hostname and remotename string.
// If no valid hostname is found, the default hostname is used. Repository name
// needs to be already validated before.
func splitHostname(name string) (hostname, remoteName string) {
	i := strings.IndexRune(name, '/')
	if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
		hostname, remoteName = DefaultHostname, name
	} else {
		hostname, remoteName = name[:i], name[i+1:]
	}
	if hostname == LegacyDefaultHostname {
		hostname = DefaultHostname
	}
	if hostname == DefaultHostname && !strings.ContainsRune(remoteName, '/') {
		remoteName = DefaultRepoPrefix + remoteName
	}
	return
}

// normalize returns a repository name in its normalized form, meaning it
// will not contain default hostname nor library/ prefix for official images.
func normalize(name string) (string, error) {
	host, remoteName := splitHostname(name)
	if strings.ToLower(remoteName) != remoteName {
		return "", errors.New("invalid reference format: repository name must be lowercase")
	}
	if host == DefaultHostname {
		if strings.HasPrefix(remoteName, DefaultRepoPrefix) {
			return strings.TrimPrefix(remoteName, DefaultRepoPrefix), nil
		}
		return remoteName, nil
	}
	return name, nil
}

// EDIT FROM DOCKER/DOCKER TO NOT IMPORT IMAGE.V1

func validateName(name string) error {
	if err := ValidateIDV1(name); err == nil {
		return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
	}
	return nil
}

var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)

// ValidateIDV1 checks whether an ID string is a valid image ID.
func ValidateIDV1(id string) error {
	if ok := validHex.MatchString(id); !ok {
		return fmt.Errorf("image ID %q is invalid", id)
	}
	return nil
}
<|endoftext|>


<commit_before>package reflector

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type fieldListingType int

const (
	fieldsAll                fieldListingType = iota
	fieldsFlattenAnonymous                    = iota
	fieldsNoFlattenAnonymous                  = iota
)

type Obj struct {
	iface interface{}

	isStruct      bool
	isPtrToStruct bool

	// If ptr to struct, this field will contain the type of that struct
	underlyingType reflect.Type

	objType reflect.Type
	objKind reflect.Kind
}

func NewFromType(ty reflect.Type) *Obj {
	return New(reflect.New(ty).Interface())
}

func New(obj interface{}) *Obj {
	o := &Obj{iface: obj}

	if obj == nil {
		o.objKind = reflect.Invalid
		return o
	} else {
		o.objType = reflect.TypeOf(obj)
		o.objKind = o.objType.Kind()
	}

	ty := o.Type()
	if ty.Kind() == reflect.Struct {
		o.isStruct = true
	}
	if ty.Kind() == reflect.Ptr && ty.Elem().Kind() == reflect.Struct {
		ty = ty.Elem()
		o.isPtrToStruct = true
	}
	o.underlyingType = ty
	return o
}

func (o *Obj) Valid() bool {
	return o.objKind == reflect.Invalid
}

// Fields returns fields. Don't list fields inside Anonymous fields as distinct fields
func (o *Obj) Fields() []ObjField {
	return o.fields(reflect.TypeOf(o.iface), fieldsNoFlattenAnonymous)
}

// FieldsFlattened returns fields. Will not list Anonymous fields but it will list fields declared in those anonymous fields
func (o Obj) FieldsFlattened() []ObjField {
	return o.fields(reflect.TypeOf(o.iface), fieldsFlattenAnonymous)
}

// FieldsFlattened returns fields. List both anonymous fields and fields declared inside anonymous fields.
func (o Obj) FieldsAll() []ObjField {
	return o.fields(reflect.TypeOf(o.iface), fieldsAll)
}

// FindDoubleFields checks if this object has declared multiple fields with a same name (by checking recursively Anonymous
// fields and their fields)
func (o Obj) FindDoubleFields() []string {
	fields := map[string]int{}
	res := []string{}
	for _, f := range o.FieldsAll() {
		counter := 0
		if counter := fields[f.name]; counter == 1 {
			res = append(res, f.name)
		}
		fields[f.name] = counter + 1
	}
	return res
}

func (o *Obj) fields(ty reflect.Type, listingType fieldListingType) []ObjField {
	fields := make([]ObjField, 0)

	if !o.Valid() {
		return fields
	}

	if ty.Kind() == reflect.Ptr {
		ty = ty.Elem()
	}

	if ty.Kind() != reflect.Struct {
		return fields // No need to populate nonstructs
	}

	for i := 0; i < ty.NumField(); i++ {
		field := ty.Field(i)

		k := field.Type.Kind()
		if isExportable(field) {
			if listingType == fieldsAll {
				fields = append(fields, *newObjField(o, field.Name))
				if k == reflect.Struct && field.Anonymous {
					fields = append(fields, o.fields(field.Type, listingType)...)
				}
			} else {
				if listingType == fieldsFlattenAnonymous && k == reflect.Struct && field.Anonymous {
					fields = append(fields, o.fields(field.Type, listingType)...)
				} else {
					fields = append(fields, *newObjField(o, field.Name))
				}
			}
		}
	}

	return fields
}

func (o Obj) IsPtr() bool {
	return o.objKind == reflect.Ptr
}

func (o Obj) IsStructOrPtrToStruct() bool {
	return o.isStruct || o.isPtrToStruct
}

func (o *Obj) Field(name string) *ObjField {
	return newObjField(o, name)
}

func (o Obj) Type() reflect.Type {
	return o.objType
}

func (o Obj) Kind() reflect.Kind {
	return o.objKind
}

func (o *Obj) Method(name string) *ObjMethod {
	return newObjMethod(o, name)
}

func (o *Obj) Methods() []ObjMethod {
	res := []ObjMethod{}
	if !o.Valid() {
		return res
	}
	ty := o.Type()
	for i := 0; i < ty.NumMethod(); i++ {
		method := ty.Method(i)
		res = append(res, *newObjMethod(o, method.Name))
	}
	return res
}

type ObjField struct {
	obj  *Obj
	name string

	valueField  reflect.Value
	structField reflect.StructField

	fieldKind reflect.Kind
	fieldType reflect.Type

	valid bool
}

func newObjField(obj *Obj, name string) *ObjField {
	res := &ObjField{
		obj:  obj,
		name: name,
	}

	res.valid = false
	res.fieldKind = reflect.Invalid
	if res.obj.IsStructOrPtrToStruct() {
		var found bool
		var valueField reflect.Value
		var structField reflect.StructField
		if res.obj.isPtrToStruct {
			valueField = reflect.ValueOf(res.obj.iface).Elem().FieldByName(res.name)
			structField, found = reflect.TypeOf(res.obj.iface).Elem().FieldByName(res.name)
		} else {
			valueField = reflect.ValueOf(res.obj.iface).FieldByName(res.name)
			structField, found = reflect.TypeOf(res.obj.iface).FieldByName(res.name)
		}
		res.valueField = valueField
		res.structField = structField
		res.valid = found && valueField.IsValid()
		if res.valid {
			res.fieldType = structField.Type
			res.fieldKind = structField.Type.Kind()
		} else {
			res.fieldKind = reflect.Invalid
		}
	}

	return res
}

func (of *ObjField) assertValid() error {
	if !of.valid {
		return fmt.Errorf("Invalid field %s", of.name)
	}
	return nil
}

func (of *ObjField) IsValid() bool {
	return of.valid
}

func (of *ObjField) Name() string {
	return of.name
}

func (of *ObjField) Kind() reflect.Kind {
	return of.fieldKind
}

func (of *ObjField) Type() reflect.Type {
	return of.fieldType
}

func (of *ObjField) Tag(tag string) (string, error) {
	if err := of.assertValid(); err != nil {
		return "", err
	}
	return of.structField.Tag.Get(tag), nil
}

func (of *ObjField) Tags() (map[string]string, error) {
	if err := of.assertValid(); err != nil {
		return nil, err
	}

	res := map[string]string{}
	tag := of.structField.Tag

	// This code is copied/modified from: reflect/type.go:
	for tag != "" {
		// Skip leading space.
		i := 0
		for i < len(tag) && tag[i] == ' ' {
			i++
		}
		tag = tag[i:]
		if tag == "" {
			break
		}

		// Scan to colon. A space, a quote or a control character is a syntax error.
		// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
		// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
		// as it is simpler to inspect the tag's bytes than the tag's runes.
		i = 0
		for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
			i++
		}
		if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
			break
		}
		name := string(tag[:i])
		tag = tag[i+1:]

		// Scan quoted string to find value.
		i = 1
		for i < len(tag) && tag[i] != '"' {
			if tag[i] == '\\' {
				i++
			}
			i++
		}
		if i >= len(tag) {
			break
		}
		qvalue := string(tag[:i+1])
		tag = tag[i+1:]

		value, err := strconv.Unquote(qvalue)
		if err != nil {
			return nil, fmt.Errorf("Cannot unquote tag %s in %T.%s: %s", name, of.obj.iface, of.name, err.Error())
		}
		res[name] = value
		/*
			if key == name {
				value, err := strconv.Unquote(qvalue)
				if err != nil {
					break
				}
				return value
			}
		*/
	}

	return res, nil
}

// TagExpanded returns the tag value "expanded" with commas
func (of *ObjField) TagExpanded(tag string) ([]string, error) {
	if err := of.assertValid(); err != nil {
		return nil, err
	}
	return strings.Split(of.structField.Tag.Get(tag), ","), nil
}

func (of *ObjField) Valid() bool {
	return of.valid
}

func (of *ObjField) Anonymous() bool {
	if err := of.assertValid(); err != nil {
		return false
	}
	field, found := of.obj.underlyingType.FieldByName(of.name)
	if !found {
		return false
	}
	return field.Anonymous
}

func (of *ObjField) Set(value interface{}) error {
	if err := of.assertValid(); err != nil {
		return err
	}

	if !of.valueField.CanSet() {
		return fmt.Errorf("Field %s in %T not settable", of.name, of.obj.iface)
	}

	of.valueField.Set(reflect.ValueOf(value))

	return nil
}

func (of *ObjField) Get() (interface{}, error) {
	if err := of.assertValid(); err != nil {
		return nil, err
	}

	return of.valueField.Interface(), nil
}

type ObjMethod struct {
	obj    *Obj
	name   string
	method reflect.Value
}

func newObjMethod(obj *Obj, name string) *ObjMethod {
	return &ObjMethod{
		obj:    obj,
		name:   name,
		method: reflect.ValueOf(obj.iface).MethodByName(name),
	}
}

func (om *ObjMethod) InTypes() []reflect.Type {
	method := reflect.ValueOf(om.obj.iface).MethodByName(om.name)
	if !method.IsValid() {
		return []reflect.Type{}
	}
	ty := method.Type()
	out := make([]reflect.Type, ty.NumIn())
	for i := 0; i < ty.NumIn(); i++ {
		out[i] = ty.In(i)
	}
	return out
}

func (om *ObjMethod) OutTypes() []reflect.Type {
	method := reflect.ValueOf(om.obj.iface).MethodByName(om.name)
	if !method.IsValid() {
		return []reflect.Type{}
	}
	ty := method.Type()
	out := make([]reflect.Type, ty.NumOut())
	for i := 0; i < ty.NumOut(); i++ {
		out[i] = ty.Out(i)
	}
	return out
}

func (om *ObjMethod) IsValid() bool {
	return om.method.IsValid()
}

// Call calls this method. Note that in the error returning value is not the error from the method call
func (om *ObjMethod) Call(args ...interface{}) (*CallResult, error) {
	if !om.method.IsValid() {
		return nil, fmt.Errorf("Invalid method %s in %T", om.name, om.obj.iface)
	}
	in := make([]reflect.Value, len(args))
	for n := range args {
		in[n] = reflect.ValueOf(args[n])
	}
	out := om.method.Call(in)
	res := make([]interface{}, len(out))
	for n := range out {
		res[n] = out[n].Interface()
	}
	return newCallResult(res), nil
}

type CallResult struct {
	Result []interface{}
	Error  error
}

func newCallResult(res []interface{}) *CallResult {
	cr := &CallResult{Result: res}
	if len(res) == 0 {
		return cr
	}
	errorCandidate := res[len(res)-1]
	if errorCandidate != nil {
		if err, is := errorCandidate.(error); is {
			cr.Error = err
		}
	}
	return cr
}

// IsError checks if the last value is a non-nil error
func (cr *CallResult) IsError() bool {
	return cr.Error != nil
}

func isExportable(field reflect.StructField) bool {
	return field.PkgPath == ""
}
<commit_msg>NPE fix<commit_after>package reflector

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

type fieldListingType int

const (
	fieldsAll                fieldListingType = iota
	fieldsFlattenAnonymous                    = iota
	fieldsNoFlattenAnonymous                  = iota
)

type Obj struct {
	iface interface{}

	isStruct      bool
	isPtrToStruct bool

	// If ptr to struct, this field will contain the type of that struct
	underlyingType reflect.Type

	objType reflect.Type
	objKind reflect.Kind
}

func NewFromType(ty reflect.Type) *Obj {
	if ty == nil {
		return New(nil)
	}
	return New(reflect.New(ty).Interface())
}

func New(obj interface{}) *Obj {
	o := &Obj{iface: obj}

	if obj == nil {
		o.objKind = reflect.Invalid
		return o
	} else {
		o.objType = reflect.TypeOf(obj)
		o.objKind = o.objType.Kind()
	}

	ty := o.Type()
	if ty.Kind() == reflect.Struct {
		o.isStruct = true
	}
	if ty.Kind() == reflect.Ptr && ty.Elem().Kind() == reflect.Struct {
		ty = ty.Elem()
		o.isPtrToStruct = true
	}
	o.underlyingType = ty
	return o
}

func (o *Obj) Valid() bool {
	return o.objKind != reflect.Invalid
}

// Fields returns fields. Don't list fields inside Anonymous fields as distinct fields
func (o *Obj) Fields() []ObjField {
	return o.fields(reflect.TypeOf(o.iface), fieldsNoFlattenAnonymous)
}

// FieldsFlattened returns fields. Will not list Anonymous fields but it will list fields declared in those anonymous fields
func (o Obj) FieldsFlattened() []ObjField {
	return o.fields(reflect.TypeOf(o.iface), fieldsFlattenAnonymous)
}

// FieldsFlattened returns fields. List both anonymous fields and fields declared inside anonymous fields.
func (o Obj) FieldsAll() []ObjField {
	return o.fields(reflect.TypeOf(o.iface), fieldsAll)
}

// FindDoubleFields checks if this object has declared multiple fields with a same name (by checking recursively Anonymous
// fields and their fields)
func (o Obj) FindDoubleFields() []string {
	fields := map[string]int{}
	res := []string{}
	for _, f := range o.FieldsAll() {
		counter := 0
		if counter := fields[f.name]; counter == 1 {
			res = append(res, f.name)
		}
		fields[f.name] = counter + 1
	}
	return res
}

func (o *Obj) fields(ty reflect.Type, listingType fieldListingType) []ObjField {
	fields := make([]ObjField, 0)

	if !o.Valid() {
		return fields
	}

	if ty.Kind() == reflect.Ptr {
		ty = ty.Elem()
	}

	if ty.Kind() != reflect.Struct {
		return fields // No need to populate nonstructs
	}

	for i := 0; i < ty.NumField(); i++ {
		field := ty.Field(i)

		k := field.Type.Kind()
		if isExportable(field) {
			if listingType == fieldsAll {
				fields = append(fields, *newObjField(o, field.Name))
				if k == reflect.Struct && field.Anonymous {
					fields = append(fields, o.fields(field.Type, listingType)...)
				}
			} else {
				if listingType == fieldsFlattenAnonymous && k == reflect.Struct && field.Anonymous {
					fields = append(fields, o.fields(field.Type, listingType)...)
				} else {
					fields = append(fields, *newObjField(o, field.Name))
				}
			}
		}
	}

	return fields
}

func (o Obj) IsPtr() bool {
	return o.objKind == reflect.Ptr
}

func (o Obj) IsStructOrPtrToStruct() bool {
	return o.isStruct || o.isPtrToStruct
}

func (o *Obj) Field(name string) *ObjField {
	return newObjField(o, name)
}

func (o Obj) Type() reflect.Type {
	return o.objType
}

func (o Obj) Kind() reflect.Kind {
	return o.objKind
}

func (o *Obj) Method(name string) *ObjMethod {
	return newObjMethod(o, name)
}

func (o *Obj) Methods() []ObjMethod {
	res := []ObjMethod{}
	if !o.Valid() {
		return res
	}
	ty := o.Type()
	for i := 0; i < ty.NumMethod(); i++ {
		method := ty.Method(i)
		res = append(res, *newObjMethod(o, method.Name))
	}
	return res
}

type ObjField struct {
	obj  *Obj
	name string

	valueField  reflect.Value
	structField reflect.StructField

	fieldKind reflect.Kind
	fieldType reflect.Type

	valid bool
}

func newObjField(obj *Obj, name string) *ObjField {
	res := &ObjField{
		obj:  obj,
		name: name,
	}

	res.valid = false
	res.fieldKind = reflect.Invalid
	if res.obj.IsStructOrPtrToStruct() {
		var found bool
		var valueField reflect.Value
		var structField reflect.StructField
		if res.obj.isPtrToStruct {
			valueField = reflect.ValueOf(res.obj.iface).Elem().FieldByName(res.name)
			structField, found = reflect.TypeOf(res.obj.iface).Elem().FieldByName(res.name)
		} else {
			valueField = reflect.ValueOf(res.obj.iface).FieldByName(res.name)
			structField, found = reflect.TypeOf(res.obj.iface).FieldByName(res.name)
		}
		res.valueField = valueField
		res.structField = structField
		res.valid = found && valueField.IsValid()
		if res.valid {
			res.fieldType = structField.Type
			res.fieldKind = structField.Type.Kind()
		} else {
			res.fieldKind = reflect.Invalid
		}
	}

	return res
}

func (of *ObjField) assertValid() error {
	if !of.valid {
		return fmt.Errorf("Invalid field %s", of.name)
	}
	return nil
}

func (of *ObjField) IsValid() bool {
	return of.valid
}

func (of *ObjField) Name() string {
	return of.name
}

func (of *ObjField) Kind() reflect.Kind {
	return of.fieldKind
}

func (of *ObjField) Type() reflect.Type {
	return of.fieldType
}

func (of *ObjField) Tag(tag string) (string, error) {
	if err := of.assertValid(); err != nil {
		return "", err
	}
	return of.structField.Tag.Get(tag), nil
}

func (of *ObjField) Tags() (map[string]string, error) {
	if err := of.assertValid(); err != nil {
		return nil, err
	}

	res := map[string]string{}
	tag := of.structField.Tag

	// This code is copied/modified from: reflect/type.go:
	for tag != "" {
		// Skip leading space.
		i := 0
		for i < len(tag) && tag[i] == ' ' {
			i++
		}
		tag = tag[i:]
		if tag == "" {
			break
		}

		// Scan to colon. A space, a quote or a control character is a syntax error.
		// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
		// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
		// as it is simpler to inspect the tag's bytes than the tag's runes.
		i = 0
		for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
			i++
		}
		if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
			break
		}
		name := string(tag[:i])
		tag = tag[i+1:]

		// Scan quoted string to find value.
		i = 1
		for i < len(tag) && tag[i] != '"' {
			if tag[i] == '\\' {
				i++
			}
			i++
		}
		if i >= len(tag) {
			break
		}
		qvalue := string(tag[:i+1])
		tag = tag[i+1:]

		value, err := strconv.Unquote(qvalue)
		if err != nil {
			return nil, fmt.Errorf("Cannot unquote tag %s in %T.%s: %s", name, of.obj.iface, of.name, err.Error())
		}
		res[name] = value
		/*
			if key == name {
				value, err := strconv.Unquote(qvalue)
				if err != nil {
					break
				}
				return value
			}
		*/
	}

	return res, nil
}

// TagExpanded returns the tag value "expanded" with commas
func (of *ObjField) TagExpanded(tag string) ([]string, error) {
	if err := of.assertValid(); err != nil {
		return nil, err
	}
	return strings.Split(of.structField.Tag.Get(tag), ","), nil
}

func (of *ObjField) Valid() bool {
	return of.valid
}

func (of *ObjField) Anonymous() bool {
	if err := of.assertValid(); err != nil {
		return false
	}
	field, found := of.obj.underlyingType.FieldByName(of.name)
	if !found {
		return false
	}
	return field.Anonymous
}

func (of *ObjField) Set(value interface{}) error {
	if err := of.assertValid(); err != nil {
		return err
	}

	if !of.valueField.CanSet() {
		return fmt.Errorf("Field %s in %T not settable", of.name, of.obj.iface)
	}

	of.valueField.Set(reflect.ValueOf(value))

	return nil
}

func (of *ObjField) Get() (interface{}, error) {
	if err := of.assertValid(); err != nil {
		return nil, err
	}

	return of.valueField.Interface(), nil
}

type ObjMethod struct {
	obj    *Obj
	name   string
	method reflect.Value
}

func newObjMethod(obj *Obj, name string) *ObjMethod {
	return &ObjMethod{
		obj:    obj,
		name:   name,
		method: reflect.ValueOf(obj.iface).MethodByName(name),
	}
}

func (om *ObjMethod) InTypes() []reflect.Type {
	method := reflect.ValueOf(om.obj.iface).MethodByName(om.name)
	if !method.IsValid() {
		return []reflect.Type{}
	}
	ty := method.Type()
	out := make([]reflect.Type, ty.NumIn())
	for i := 0; i < ty.NumIn(); i++ {
		out[i] = ty.In(i)
	}
	return out
}

func (om *ObjMethod) OutTypes() []reflect.Type {
	method := reflect.ValueOf(om.obj.iface).MethodByName(om.name)
	if !method.IsValid() {
		return []reflect.Type{}
	}
	ty := method.Type()
	out := make([]reflect.Type, ty.NumOut())
	for i := 0; i < ty.NumOut(); i++ {
		out[i] = ty.Out(i)
	}
	return out
}

func (om *ObjMethod) IsValid() bool {
	return om.method.IsValid()
}

// Call calls this method. Note that in the error returning value is not the error from the method call
func (om *ObjMethod) Call(args ...interface{}) (*CallResult, error) {
	if !om.method.IsValid() {
		return nil, fmt.Errorf("Invalid method %s in %T", om.name, om.obj.iface)
	}
	in := make([]reflect.Value, len(args))
	for n := range args {
		in[n] = reflect.ValueOf(args[n])
	}
	out := om.method.Call(in)
	res := make([]interface{}, len(out))
	for n := range out {
		res[n] = out[n].Interface()
	}
	return newCallResult(res), nil
}

type CallResult struct {
	Result []interface{}
	Error  error
}

func newCallResult(res []interface{}) *CallResult {
	cr := &CallResult{Result: res}
	if len(res) == 0 {
		return cr
	}
	errorCandidate := res[len(res)-1]
	if errorCandidate != nil {
		if err, is := errorCandidate.(error); is {
			cr.Error = err
		}
	}
	return cr
}

// IsError checks if the last value is a non-nil error
func (cr *CallResult) IsError() bool {
	return cr.Error != nil
}

func isExportable(field reflect.StructField) bool {
	return field.PkgPath == ""
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"src/todo"
	"strings"
)

const todoFilename = "todo.json"
const todoFileBin = "todo.gob"
const lastModified = "14 Jan 2022"

var verboseFlag = flag.Bool("v", false, "Set verbose mode.")
var task = flag.String("task", "", "Task to be added to the ToDo list.")
var complete = flag.Int("complete", 0, "Item to be completed.") // here, 0 means NTD.  That's why we have to start at 1 for item numbers.
var listFlag = flag.Bool("list", false, "List all tasks to the display.")

func main() {
	flag.Parse()
	if *verboseFlag {
		fmt.Printf(" todo last modified %s.  It will display and manage a todo list.\n", lastModified)

	}

	l := todo.ListType{}
	err := l.LoadJSON(todoFilename) // if file doesn't exist, this does not return an error.
	if err != nil {
		fmt.Fprintf(os.Stderr, " Error returned while reading %s is %v\n", todoFilename, err)
		fmt.Print(" Should I exit? ")
		var ans string
		fmt.Scanln(&ans)
		if strings.HasPrefix(strings.ToLower(ans), "y") {
			os.Exit(1)
		}
	}

	switch {
	case *listFlag:
		for _, item := range l {
			if !item.Done {
				fmt.Println(item.Task)
			}
		}
	case *complete > 0:
		err = l.Complete(*complete)
		if err != nil {
			fmt.Fprintf(os.Stderr, " Item number %d cannot be completed because %v\n", *complete, err)
		}

		err = l.SaveJSON(todoFilename)
		if err != nil {
			fmt.Fprintf(os.Stderr, " List could not be saved in json because %v\n", err)
		}
		err = l.SaveBinary(todoFileBin)
		if err != nil {
			fmt.Fprintf(os.Stderr, " List could not be saved in binary format because %v\n", err)
		}
	case *task != "":
		l.Add(*task)
		err = l.SaveJSON(todoFilename)
		if err != nil {
			fmt.Fprintf(os.Stderr, " List could not be saved in JSON because %v \n", err)
		}
		err = l.SaveBinary(todoFileBin)
		if err != nil {
			fmt.Fprintf(os.Stderr, " List could not be saved in binary format because %v \n", err)
		}
	default:
		fmt.Fprintf(os.Stderr, " No valid option was set.\n")
	}
}
<commit_msg>01/14/2022 07:12:44 PM todo/cmd/todo/main.go<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"src/todo"
	"strings"
)

const todoFilename = "todo.json"
const todoFileBin = "todo.gob"
const lastModified = "14 Jan 2022"

var verboseFlag = flag.Bool("v", false, "Set verbose mode.")
var task = flag.String("task", "", "Task to be added to the ToDo list.")
var complete = flag.Int("complete", 0, "Item to be completed.") // here, 0 means NTD.  That's why we have to start at 1 for item numbers.
var listFlag = flag.Bool("list", false, "List all tasks to the display.")

func main() {
	flag.Parse()
	if *verboseFlag {
		fmt.Printf(" todo last modified %s.  It will display and manage a todo list.\n", lastModified)

	}

	l := todo.ListType{}
	err := l.LoadJSON(todoFilename) // if file doesn't exist, this does not return an error.
	if err != nil {
		fmt.Fprintf(os.Stderr, " Error returned while reading %s is %v\n", todoFilename, err)
		fmt.Print(" Should I exit? ")
		var ans string
		fmt.Scanln(&ans)
		if strings.HasPrefix(strings.ToLower(ans), "y") {
			os.Exit(1)
		}
	}

	switch {
	case *listFlag:
		for _, item := range l {
			if !item.Done {
				fmt.Printf(" Not done: %s\n", item.Task)
			}
		}
		fmt.Println()
		for _, item := range l {
			if item.Done {
				fmt.Printf(" Done: %s was completed on %s\n", item.Task, item.CompletedAt.Format("Jan-02-2006 15:04:05"))
			}
		}
	case *complete > 0:
		err = l.Complete(*complete)
		if err != nil {
			fmt.Fprintf(os.Stderr, " Item number %d cannot be completed because %v\n", *complete, err)
		}

		err = l.SaveJSON(todoFilename)
		if err != nil {
			fmt.Fprintf(os.Stderr, " List could not be saved in json because %v\n", err)
		}
		err = l.SaveBinary(todoFileBin)
		if err != nil {
			fmt.Fprintf(os.Stderr, " List could not be saved in binary format because %v\n", err)
		}
	case *task != "":
		l.Add(*task)
		err = l.SaveJSON(todoFilename)
		if err != nil {
			fmt.Fprintf(os.Stderr, " List could not be saved in JSON because %v \n", err)
		}
		err = l.SaveBinary(todoFileBin)
		if err != nil {
			fmt.Fprintf(os.Stderr, " List could not be saved in binary format because %v \n", err)
		}
	default:
		fmt.Fprintf(os.Stderr, " No valid option was set.\n")
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

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

	"github.com/google/syzkaller/pkg/hash"
	"github.com/google/syzkaller/pkg/mgrconfig"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/pkg/report"
	"github.com/google/syzkaller/pkg/vcs"
)

var (
	flagOS        = flag.String("os", runtime.GOOS, "target os")
	flagArch      = flag.String("arch", runtime.GOARCH, "target arch")
	flagKernelObj = flag.String("kernel_obj", ".", "path to kernel build/obj dir")
	flagKernelSrc = flag.String("kernel_src", "", "path to kernel sources (defaults to kernel_obj)")
	flagOutDir    = flag.String("outdir", "", "output directory")
)

func main() {
	flag.Parse()
	if len(flag.Args()) != 1 {
		fmt.Fprintf(os.Stderr, "usage: syz-symbolize [flags] kernel_log_file\n")
		flag.PrintDefaults()
		os.Exit(1)
	}
	cfg := &mgrconfig.Config{
		Derived: mgrconfig.Derived{
			TargetOS:     *flagOS,
			TargetArch:   *flagArch,
			TargetVMArch: *flagArch,
		},
		KernelObj: *flagKernelObj,
		KernelSrc: *flagKernelSrc,
	}
	cfg.CompleteKernelDirs()
	reporter, err := report.NewReporter(cfg)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create reporter: %v\n", err)
		os.Exit(1)
	}
	text, err := ioutil.ReadFile(flag.Args()[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to open input file: %v\n", err)
		os.Exit(1)
	}
	reps := report.ParseAll(reporter, text)
	if len(reps) == 0 {
		rep := &report.Report{Report: text}
		if err := reporter.Symbolize(rep); err != nil {
			fmt.Fprintf(os.Stderr, "failed to symbolize report: %v\n", err)
			os.Exit(1)
		}
		os.Stdout.Write(rep.Report)
		return
	}
	for _, rep := range reps {
		if *flagOutDir != "" {
			saveCrash(rep, *flagOutDir)
		}
		if err := reporter.Symbolize(rep); err != nil {
			fmt.Fprintf(os.Stderr, "failed to symbolize report: %v\n", err)
		}
		fmt.Printf("TITLE: %v\n", rep.Title)
		fmt.Printf("CORRUPTED: %v (%v)\n", rep.Corrupted, rep.CorruptedReason)
		fmt.Printf("MAINTAINERS (TO): %v\n", rep.Recipients.GetEmails(vcs.To))
		fmt.Printf("MAINTAINERS (CC): %v\n", rep.Recipients.GetEmails(vcs.Cc))
		fmt.Printf("\n")
		os.Stdout.Write(rep.Report)
		fmt.Printf("\n\n")
	}
}

func saveCrash(rep *report.Report, path string) {
	sig := hash.Hash([]byte(rep.Title))
	id := sig.String()
	dir := filepath.Join(path, id)
	osutil.MkdirAll(dir)
	if err := osutil.WriteFile(filepath.Join(dir, "description"), []byte(rep.Title+"\n")); err != nil {
		fmt.Fprintf(os.Stderr, "failed to write description: %v\n", err)
		os.Exit(1)
	}

	if err := osutil.WriteFile(filepath.Join(dir, "log"), rep.Output); err != nil {
		fmt.Fprintf(os.Stderr, "failed to write log: %v\n", err)
		os.Exit(1)
	}

	if len(rep.Report) > 0 {
		if err := osutil.WriteFile(filepath.Join(dir, "report"), rep.Report); err != nil {
			fmt.Fprintf(os.Stderr, "failed to write report: %v\n", err)
			os.Exit(1)
		}
	}
}
<commit_msg>tools/syz-symbolize: fix config loading<commit_after>// Copyright 2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package main

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

	"github.com/google/syzkaller/pkg/hash"
	"github.com/google/syzkaller/pkg/mgrconfig"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/pkg/report"
	"github.com/google/syzkaller/pkg/vcs"
)

var (
	flagOS        = flag.String("os", runtime.GOOS, "target os")
	flagArch      = flag.String("arch", runtime.GOARCH, "target arch")
	flagKernelObj = flag.String("kernel_obj", ".", "path to kernel build/obj dir")
	flagKernelSrc = flag.String("kernel_src", "", "path to kernel sources (defaults to kernel_obj)")
	flagOutDir    = flag.String("outdir", "", "output directory")
)

func main() {
	flag.Parse()
	if len(flag.Args()) != 1 {
		fmt.Fprintf(os.Stderr, "usage: syz-symbolize [flags] kernel_log_file\n")
		flag.PrintDefaults()
		os.Exit(1)
	}
	cfg, err := mgrconfig.LoadPartialData([]byte(`{
		"kernel_obj": "` + *flagKernelObj + `",
		"kernel_src": "` + *flagKernelSrc + `",
		"target": "` + *flagOS + "/" + *flagArch + `"
}`))
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
	cfg.CompleteKernelDirs()
	reporter, err := report.NewReporter(cfg)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create reporter: %v\n", err)
		os.Exit(1)
	}
	text, err := ioutil.ReadFile(flag.Args()[0])
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to open input file: %v\n", err)
		os.Exit(1)
	}
	reps := report.ParseAll(reporter, text)
	if len(reps) == 0 {
		rep := &report.Report{Report: text}
		if err := reporter.Symbolize(rep); err != nil {
			fmt.Fprintf(os.Stderr, "failed to symbolize report: %v\n", err)
			os.Exit(1)
		}
		os.Stdout.Write(rep.Report)
		return
	}
	for _, rep := range reps {
		if *flagOutDir != "" {
			saveCrash(rep, *flagOutDir)
		}
		if err := reporter.Symbolize(rep); err != nil {
			fmt.Fprintf(os.Stderr, "failed to symbolize report: %v\n", err)
		}
		fmt.Printf("TITLE: %v\n", rep.Title)
		fmt.Printf("CORRUPTED: %v (%v)\n", rep.Corrupted, rep.CorruptedReason)
		fmt.Printf("MAINTAINERS (TO): %v\n", rep.Recipients.GetEmails(vcs.To))
		fmt.Printf("MAINTAINERS (CC): %v\n", rep.Recipients.GetEmails(vcs.Cc))
		fmt.Printf("\n")
		os.Stdout.Write(rep.Report)
		fmt.Printf("\n\n")
	}
}

func saveCrash(rep *report.Report, path string) {
	sig := hash.Hash([]byte(rep.Title))
	id := sig.String()
	dir := filepath.Join(path, id)
	osutil.MkdirAll(dir)
	if err := osutil.WriteFile(filepath.Join(dir, "description"), []byte(rep.Title+"\n")); err != nil {
		fmt.Fprintf(os.Stderr, "failed to write description: %v\n", err)
		os.Exit(1)
	}

	if err := osutil.WriteFile(filepath.Join(dir, "log"), rep.Output); err != nil {
		fmt.Fprintf(os.Stderr, "failed to write log: %v\n", err)
		os.Exit(1)
	}

	if len(rep.Report) > 0 {
		if err := osutil.WriteFile(filepath.Join(dir, "report"), rep.Report); err != nil {
			fmt.Fprintf(os.Stderr, "failed to write report: %v\n", err)
			os.Exit(1)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// 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 probes

import (
	"testing"

	"github.com/golang/protobuf/proto"
	"github.com/google/cloudprober/metrics"
	"github.com/google/cloudprober/probes/options"
	"github.com/google/cloudprober/probes/testdata"
	"google3/go/context/context"
)

type testProbe struct{}

func (p *testProbe) Init(name string, opts *options.Options) error { return nil }

func (p *testProbe) Start(ctx context.Context, dataChan chan *metrics.EventMetrics) {}

func TestGetExtensionProbe(t *testing.T) {
	probeDef := &probes.ProbeDef{}

	// This has the same effect as using the following in your config:
	// probe {
	//    name: "run_fancy_probe_for_x"
	//    targets: ...
	//    ...
	//    [cloudprober.probes.testdata.fancy_probe] {
	//      name: "fancy"
	//    }
	// }
	err := proto.SetExtension(probeDef, testdata.E_FancyProbe, &testdata.FancyProbe{Name: proto.String("fancy")})
	if err != nil {
		t.Fatalf("error setting up extension in test probe proto: %v", err)
	}
	probeEx, _, err := getExtensionProbe(probeDef)
	if err == nil {
		t.Errorf("Expected error in building probe from extensions, got nil. probe: %v", probeEx)
	}

	// Register our test probe type and try again.
	RegisterProbeType(200, func() Probe {
		return &testProbe{}
	})
	probeEx, value, err := getExtensionProbe(probeDef)
	if err != nil {
		t.Errorf("Got error in building probe from extensions: %v", err)
	}
	_, ok := value.(*testdata.FancyProbe)
	if !ok {
		t.Errorf("Extension probe conf (%v) is not of type *testdata.FancyProbe", value)
	}
	_, ok = probeEx.(*testProbe)
	if !ok {
		t.Errorf("Extension probe (%v) is not of type *testProbe", probeEx)
	}
}
<commit_msg>probes_test: Use standard library's context package.<commit_after>// Copyright 2017 Google Inc.
//
// 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 probes

import (
	"context"
	"testing"

	"github.com/golang/protobuf/proto"
	"github.com/google/cloudprober/metrics"
	"github.com/google/cloudprober/probes/options"
	"github.com/google/cloudprober/probes/testdata"
)

type testProbe struct{}

func (p *testProbe) Init(name string, opts *options.Options) error { return nil }

func (p *testProbe) Start(ctx context.Context, dataChan chan *metrics.EventMetrics) {}

func TestGetExtensionProbe(t *testing.T) {
	probeDef := &probes.ProbeDef{}

	// This has the same effect as using the following in your config:
	// probe {
	//    name: "run_fancy_probe_for_x"
	//    targets: ...
	//    ...
	//    [cloudprober.probes.testdata.fancy_probe] {
	//      name: "fancy"
	//    }
	// }
	err := proto.SetExtension(probeDef, testdata.E_FancyProbe, &testdata.FancyProbe{Name: proto.String("fancy")})
	if err != nil {
		t.Fatalf("error setting up extension in test probe proto: %v", err)
	}
	probeEx, _, err := getExtensionProbe(probeDef)
	if err == nil {
		t.Errorf("Expected error in building probe from extensions, got nil. probe: %v", probeEx)
	}

	// Register our test probe type and try again.
	RegisterProbeType(200, func() Probe {
		return &testProbe{}
	})
	probeEx, value, err := getExtensionProbe(probeDef)
	if err != nil {
		t.Errorf("Got error in building probe from extensions: %v", err)
	}
	_, ok := value.(*testdata.FancyProbe)
	if !ok {
		t.Errorf("Extension probe conf (%v) is not of type *testdata.FancyProbe", value)
	}
	_, ok = probeEx.(*testProbe)
	if !ok {
		t.Errorf("Extension probe (%v) is not of type *testProbe", probeEx)
	}
}
<|endoftext|>


<commit_before>package main
import "fmt"

func main() {

 fmt.Println("test ifconfig")

 }
<commit_msg>//add new fmt.Prinln to ifconf.go<commit_after>package main
import "fmt"

func main() {

 fmt.Println("test config")
 fmt.Println("add new ifconfig")

 }
<|endoftext|>


<commit_before>// Copyright 2019 The Cloud Robotics 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 main

import (
	"context"
	"strings"
	"testing"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/fake"
)

const (
	defaultCorefileBeforeMinikube121 = `.:53 {
    whoami
}
`
	modifiedCorefileBeforeMinikube121 = `.:53 {
    hosts hosts metadata.google.internal {
        169.254.169.254 metadata.google.internal
        fallthrough
    }
    whoami
}
`
	defaultCorefileAfterMinikube121 = `.:53 {
    whoami
    hosts {
        127.0.0.1 host.minikube.internal
        fallthrough
    }
}
`
	modifiedCorefileAfterMinikube121 = `.:53 {
    whoami
    hosts hosts metadata.google.internal host.minikube.internal {
        169.254.169.254 metadata.google.internal
        127.0.0.1 host.minikube.internal
        fallthrough
    }
}
`
	differentIp = "1.2.3.456"
)

func createCorefile(t *testing.T, k8s kubernetes.Interface, corefileData string) {
	if _, err := k8s.CoreV1().ConfigMaps(configMapNamespace).Create(
		context.Background(),
		&v1.ConfigMap{
			Data: map[string]string{
				corefileName: corefileData,
			},
			ObjectMeta: metav1.ObjectMeta{
				Name: configMapName,
			},
		},
		metav1.CreateOptions{}); err != nil {
		t.Errorf("error creating ConfigMap %s: %v", configMapName, err)
	}
}

func readCorefile(t *testing.T, k8s kubernetes.Interface) string {
	cm, err := k8s.CoreV1().ConfigMaps(configMapNamespace).Get(context.Background(), configMapName, metav1.GetOptions{})
	if err != nil {
		t.Errorf("error reading ConfigMap coredns: %v", err)
		return ""
	}
	data, ok := cm.Data[corefileName]
	if !ok {
		t.Errorf("ConfigMap %s doesn't contain key %s", configMapName, corefileName)
		return ""
	}
	return data
}

func TestPatchCorefile(t *testing.T) {
	tests := []struct {
		desc  string
		input string
		want  string
	}{
		{
			"default corefile without host.minikube.internal entry",
			defaultCorefileBeforeMinikube121,
			modifiedCorefileBeforeMinikube121,
		},
		{
			"default corefile with host.minikube.internal entry",
			defaultCorefileAfterMinikube121,
			modifiedCorefileAfterMinikube121,
		},
		{
			"default corefile with host.minikube.internal entry, different IP",
			strings.Replace(defaultCorefileAfterMinikube121, "127.0.0.1", differentIp, 1),
			strings.Replace(modifiedCorefileAfterMinikube121, "127.0.0.1", differentIp, 1),
		},
	}

	for _, tc := range tests {
		t.Run(tc.desc, func(t *testing.T) {
			ctx := context.Background()
			k8s := fake.NewSimpleClientset()
			createCorefile(t, k8s, tc.input)

			if err := PatchCorefile(ctx, k8s); err != nil {
				t.Errorf("error in PatchCorefile: %v", err)
			}
			if got := readCorefile(t, k8s); got != tc.want {
				t.Errorf(`want readCorefile(t, k8s) = %q, got %q`, tc.want, got)
			}

			// Check that a second patch has no effect.
			if err := PatchCorefile(ctx, k8s); err != nil {
				t.Errorf("error in second PatchCorefile: %v", err)
			}
			if got := readCorefile(t, k8s); got != tc.want {
				t.Errorf(`after second patch, want readCorefile(t, k8s) = %q, got %q`, tc.want, got)
			}

			// Check that reverting undoes the change.
			if err := RevertCorefile(ctx, k8s); err != nil {
				t.Errorf("error in RevertCorefile: %v", err)
			}
			if got := readCorefile(t, k8s); got != tc.input {
				t.Errorf(`after revert, want readCorefile(t, k8s) = %q, got %q`, tc.input, got)
			}

			// Check that a second revert has no effect.
			if err := RevertCorefile(ctx, k8s); err != nil {
				t.Errorf("error in second RevertCorefile: %v", err)
			}
			if got := readCorefile(t, k8s); got != tc.input {
				t.Errorf(`after second revert, want readCorefile(t, k8s) = %q, got %q`, tc.input, got)
			}
		})
	}
}
<commit_msg>Add a unit test for an unexpected input<commit_after>// Copyright 2019 The Cloud Robotics 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 main

import (
	"context"
	"strings"
	"testing"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/fake"
)

const (
	defaultCorefileBeforeMinikube121 = `.:53 {
    whoami
}
`
	modifiedCorefileBeforeMinikube121 = `.:53 {
    hosts hosts metadata.google.internal {
        169.254.169.254 metadata.google.internal
        fallthrough
    }
    whoami
}
`
	defaultCorefileAfterMinikube121 = `.:53 {
    whoami
    hosts {
        127.0.0.1 host.minikube.internal
        fallthrough
    }
}
`
	modifiedCorefileAfterMinikube121 = `.:53 {
    whoami
    hosts hosts metadata.google.internal host.minikube.internal {
        169.254.169.254 metadata.google.internal
        127.0.0.1 host.minikube.internal
        fallthrough
    }
}
`
	defaultCorefileUnexpected = `.:53 {
    whoami
    hosts {
        127.0.0.1 host2.minikube.internal
        fallthrough
    }
}
`
	differentIp = "1.2.3.456"
)

func createCorefile(t *testing.T, k8s kubernetes.Interface, corefileData string) {
	if _, err := k8s.CoreV1().ConfigMaps(configMapNamespace).Create(
		context.Background(),
		&v1.ConfigMap{
			Data: map[string]string{
				corefileName: corefileData,
			},
			ObjectMeta: metav1.ObjectMeta{
				Name: configMapName,
			},
		},
		metav1.CreateOptions{}); err != nil {
		t.Errorf("error creating ConfigMap %s: %v", configMapName, err)
	}
}

func readCorefile(t *testing.T, k8s kubernetes.Interface) string {
	cm, err := k8s.CoreV1().ConfigMaps(configMapNamespace).Get(context.Background(), configMapName, metav1.GetOptions{})
	if err != nil {
		t.Errorf("error reading ConfigMap coredns: %v", err)
		return ""
	}
	data, ok := cm.Data[corefileName]
	if !ok {
		t.Errorf("ConfigMap %s doesn't contain key %s", configMapName, corefileName)
		return ""
	}
	return data
}

func TestPatchCorefile(t *testing.T) {
	tests := []struct {
		desc  string
		input string
		want  string
	}{
		{
			"default corefile without host.minikube.internal entry",
			defaultCorefileBeforeMinikube121,
			modifiedCorefileBeforeMinikube121,
		},
		{
			"default corefile with host.minikube.internal entry",
			defaultCorefileAfterMinikube121,
			modifiedCorefileAfterMinikube121,
		},
		{
			"default corefile with host.minikube.internal entry, different IP",
			strings.Replace(defaultCorefileAfterMinikube121, "127.0.0.1", differentIp, 1),
			strings.Replace(modifiedCorefileAfterMinikube121, "127.0.0.1", differentIp, 1),
		},
	}

	for _, tc := range tests {
		t.Run(tc.desc, func(t *testing.T) {
			ctx := context.Background()
			k8s := fake.NewSimpleClientset()
			createCorefile(t, k8s, tc.input)

			if err := PatchCorefile(ctx, k8s); err != nil {
				t.Errorf("error in PatchCorefile: %v", err)
			}
			if got := readCorefile(t, k8s); got != tc.want {
				t.Errorf(`want readCorefile(t, k8s) = %q, got %q`, tc.want, got)
			}

			// Check that a second patch has no effect.
			if err := PatchCorefile(ctx, k8s); err != nil {
				t.Errorf("error in second PatchCorefile: %v", err)
			}
			if got := readCorefile(t, k8s); got != tc.want {
				t.Errorf(`after second patch, want readCorefile(t, k8s) = %q, got %q`, tc.want, got)
			}

			// Check that reverting undoes the change.
			if err := RevertCorefile(ctx, k8s); err != nil {
				t.Errorf("error in RevertCorefile: %v", err)
			}
			if got := readCorefile(t, k8s); got != tc.input {
				t.Errorf(`after revert, want readCorefile(t, k8s) = %q, got %q`, tc.input, got)
			}

			// Check that a second revert has no effect.
			if err := RevertCorefile(ctx, k8s); err != nil {
				t.Errorf("error in second RevertCorefile: %v", err)
			}
			if got := readCorefile(t, k8s); got != tc.input {
				t.Errorf(`after second revert, want readCorefile(t, k8s) = %q, got %q`, tc.input, got)
			}
		})
	}
}

func TestPatchCorefileUnexpected(t *testing.T) {
	ctx := context.Background()
	k8s := fake.NewSimpleClientset()
	createCorefile(t, k8s, defaultCorefileUnexpected)

	if err := PatchCorefile(ctx, k8s); err == nil {
		t.Error("PatchCorefile() succeeded with invalid input, wanted error")
	}
	if got := readCorefile(t, k8s); got != defaultCorefileUnexpected {
		t.Errorf(`unexpected input should not be modified, want readCorefile(t, k8s) = %q, got %q`, defaultCorefileUnexpected, got)
	}
}
<|endoftext|>


<commit_before>package pluginutil

import (
	"sync"

	"github.com/codegangsta/cli"
)

//NewContext - convenience method to construct a valid cli.Context within a
//plugin
func NewContext(args []string, myflags []cli.Flag) (context *cli.Context) {
	var wg sync.WaitGroup
	command := cli.Command{
		Flags: myflags,
	}
	app := cli.NewApp()
	app.Name = args[0]
	app.HideHelp = true
	app.Flags = myflags
	app.Action = func(c *cli.Context) error {
		defer wg.Done()
		c.Command = command
		context = c

		return nil
	}

	wg.Add(1)
	app.Run(args)
	wg.Wait()
	return context
}
<commit_msg>Simplify pluginutil.NewContext<commit_after>package pluginutil

import "github.com/codegangsta/cli"

//NewContext - convenience method to construct a valid cli.Context within a
//plugin
func NewContext(args []string, myflags []cli.Flag) (context *cli.Context) {
	command := cli.Command{
		Flags: myflags,
	}
	app := cli.NewApp()
	app.Name = args[0]
	app.HideHelp = true
	app.Flags = myflags
	app.Action = func(c *cli.Context) error {
		c.Command = command
		context = c
		return nil
	}
	app.Run(args)
	return context
}
<|endoftext|>


<commit_before>package chat

import (
	"github.com/gempir/gempbot/pkg/config"
	"github.com/gempir/gempbot/pkg/log"
	"github.com/gempir/go-twitch-irc/v2"
)

type ChatClient struct {
	ircClient *twitch.Client
	cfg       *config.Config
	connected chan bool
}

func NewClient(cfg *config.Config) *ChatClient {
	return &ChatClient{
		cfg:       cfg,
		connected: make(chan bool),
		ircClient: twitch.NewClient(cfg.Username, cfg.OAuth),
	}
}

func (c *ChatClient) Say(channel string, message string) {
	c.ircClient.Say(channel, message)
}

func (c *ChatClient) JoinBot(channel string) {
	c.Say(c.cfg.Username, "JOIN "+c.cfg.Environment+" "+channel)
}

func (c *ChatClient) PartBot(channel string) {
	c.Say(c.cfg.Username, "PART "+c.cfg.Environment+" "+channel)
}

func (c *ChatClient) Join(channel string) {
	log.Infof("JOIN %s", channel)
	c.ircClient.Join(channel)
}

func (c *ChatClient) Part(channel string) {
	log.Infof("PART %s", channel)
	c.ircClient.Depart(channel)
}

func (c *ChatClient) WaitForConnect() {
	<-c.connected
}

func (c *ChatClient) Connect() {
	c.ircClient.OnConnect(func() {
		log.Info("connected to Twitch IRC")
		go func() {
			c.connected <- true
		}()
	})

	c.ircClient.OnRoomStateMessage(func(roomStateMessage twitch.RoomStateMessage) {
		log.Infof("#%s roomstate %v", roomStateMessage.Channel, roomStateMessage.State)
	})

	err := c.ircClient.Connect()
	if err != nil {
		log.Error(err)
	}
}

func (c *ChatClient) SetOnPrivateMessage(f func(twitch.PrivateMessage)) {
	c.ircClient.OnPrivateMessage(f)
}
<commit_msg>join after connection<commit_after>package chat

import (
	"github.com/gempir/gempbot/pkg/config"
	"github.com/gempir/gempbot/pkg/log"
	"github.com/gempir/go-twitch-irc/v2"
)

type ChatClient struct {
	ircClient *twitch.Client
	cfg       *config.Config
	connected chan bool
}

func NewClient(cfg *config.Config) *ChatClient {
	return &ChatClient{
		cfg:       cfg,
		connected: make(chan bool),
		ircClient: twitch.NewClient(cfg.Username, cfg.OAuth),
	}
}

func (c *ChatClient) Say(channel string, message string) {
	c.ircClient.Say(channel, message)
}

func (c *ChatClient) JoinBot(channel string) {
	c.Say(c.cfg.Username, "JOIN "+c.cfg.Environment+" "+channel)
}

func (c *ChatClient) PartBot(channel string) {
	c.Say(c.cfg.Username, "PART "+c.cfg.Environment+" "+channel)
}

func (c *ChatClient) Join(channel string) {
	c.WaitForConnect()
	log.Infof("JOIN %s", channel)
	c.ircClient.Join(channel)
}

func (c *ChatClient) Part(channel string) {
	log.Infof("PART %s", channel)
	c.ircClient.Depart(channel)
}

func (c *ChatClient) WaitForConnect() {
	<-c.connected
}

func (c *ChatClient) Connect() {
	c.ircClient.OnConnect(func() {
		log.Info("connected to Twitch IRC")
		go func() {
			c.connected <- true
		}()
	})

	c.ircClient.OnRoomStateMessage(func(roomStateMessage twitch.RoomStateMessage) {
		log.Infof("#%s roomstate %v", roomStateMessage.Channel, roomStateMessage.State)
	})

	err := c.ircClient.Connect()
	if err != nil {
		log.Error(err)
	}
}

func (c *ChatClient) SetOnPrivateMessage(f func(twitch.PrivateMessage)) {
	c.ircClient.OnPrivateMessage(f)
}
<|endoftext|>


<commit_before>package ecsutil

import (
	"fmt"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/remind101/pkg/trace"
	"golang.org/x/net/context"
)

// ECS represents our ECS client interface.
type ECS interface {
	// Task Definitions
	RegisterTaskDefinition(context.Context, *ecs.RegisterTaskDefinitionInput) (*ecs.RegisterTaskDefinitionOutput, error)
	DescribeTaskDefinition(context.Context, *ecs.DescribeTaskDefinitionInput) (*ecs.DescribeTaskDefinitionOutput, error)

	// Services
	CreateService(context.Context, *ecs.CreateServiceInput) (*ecs.CreateServiceOutput, error)
	DeleteService(context.Context, *ecs.DeleteServiceInput) (*ecs.DeleteServiceOutput, error)
	UpdateService(context.Context, *ecs.UpdateServiceInput) (*ecs.UpdateServiceOutput, error)
	ListServicesPages(context.Context, *ecs.ListServicesInput, func(*ecs.ListServicesOutput, bool) bool) error
	DescribeServices(context.Context, *ecs.DescribeServicesInput) (*ecs.DescribeServicesOutput, error)

	// Tasks
	ListTasksPages(context.Context, *ecs.ListTasksInput, func(*ecs.ListTasksOutput, bool) bool) error
	StopTask(context.Context, *ecs.StopTaskInput) (*ecs.StopTaskOutput, error)
	DescribeTasks(context.Context, *ecs.DescribeTasksInput) (*ecs.DescribeTasksOutput, error)
	RunTask(context.Context, *ecs.RunTaskInput) (*ecs.RunTaskOutput, error)
}

// newECSClient builds a new ECS client with autopagination and tracing.
func newECSClient(p client.ConfigProvider) ECS {
	ecs := ecs.New(p)
	return &limitedClient{
		ECS: &ecsClient{ECS: ecs},
	}
}

// ecsClient is a base ECS client implementation
type ecsClient struct {
	*ecs.ECS

	// a timer used to throttle the RegisterTaskDefinition calls. ECS only
	// allows 60/min. See http://docs.aws.amazon.com/AmazonECS/latest/developerguide/service_limits.html
	tdThrottle *time.Ticker
}

func (c *ecsClient) CreateService(ctx context.Context, input *ecs.CreateServiceInput) (*ecs.CreateServiceOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.CreateService(input)
	done(err, "CreateService", "service-name", stringField(input.ServiceName), "desired-count", intField(input.DesiredCount), "task-definition", stringField(input.TaskDefinition))
	return resp, err
}

func (c *ecsClient) DeleteService(ctx context.Context, input *ecs.DeleteServiceInput) (*ecs.DeleteServiceOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.DeleteService(input)
	done(err, "DeleteService", "service-name", stringField(input.Service))
	return resp, err
}

func (c *ecsClient) UpdateService(ctx context.Context, input *ecs.UpdateServiceInput) (*ecs.UpdateServiceOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.UpdateService(input)
	done(err, "UpdateService", "service-name", stringField(input.Service), "desired-count", intField(input.DesiredCount), "task-definition", stringField(input.TaskDefinition))
	return resp, err
}

func (c *ecsClient) RegisterTaskDefinition(ctx context.Context, input *ecs.RegisterTaskDefinitionInput) (*ecs.RegisterTaskDefinitionOutput, error) {
	if c.tdThrottle == nil {
		// Only allow 1 task definition per second.
		c.tdThrottle = time.NewTicker(time.Second)
	}

	<-c.tdThrottle.C

	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.RegisterTaskDefinition(input)
	done(err, "RegisterTaskDefinition", "family", stringField(input.Family))
	return resp, err
}

func (c *ecsClient) DescribeTaskDefinition(ctx context.Context, input *ecs.DescribeTaskDefinitionInput) (*ecs.DescribeTaskDefinitionOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.DescribeTaskDefinition(input)
	done(err, "DescribeTaskDefinition", "task-definition", stringField(input.TaskDefinition))
	return resp, err
}

func (c *ecsClient) ListServicesPages(ctx context.Context, input *ecs.ListServicesInput, fn func(*ecs.ListServicesOutput, bool) bool) error {
	ctx, done := trace.Trace(ctx)
	err := c.ECS.ListServicesPages(input, fn)
	done(err, "ListServicesPages")
	return err
}

func (c *ecsClient) DescribeServices(ctx context.Context, input *ecs.DescribeServicesInput) (*ecs.DescribeServicesOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.DescribeServices(input)
	done(err, "DescribeServices", "services", len(input.Services))
	return resp, err
}

func (c *ecsClient) ListTasksPages(ctx context.Context, input *ecs.ListTasksInput, fn func(*ecs.ListTasksOutput, bool) bool) error {
	ctx, done := trace.Trace(ctx)
	err := c.ECS.ListTasksPages(input, fn)
	done(err, "ListTasksPages")
	return err
}

func (c *ecsClient) DescribeTasks(ctx context.Context, input *ecs.DescribeTasksInput) (*ecs.DescribeTasksOutput, error) {
	return c.describeTasks(ctx, input)
}

func (c *ecsClient) describeTasks(ctx context.Context, input *ecs.DescribeTasksInput) (*ecs.DescribeTasksOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.DescribeTasks(input)
	done(err, "DescribeTasks", "tasks", len(input.Tasks))
	return resp, err
}

func (c *ecsClient) StopTask(ctx context.Context, input *ecs.StopTaskInput) (*ecs.StopTaskOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.StopTask(input)
	done(err, "StopTask", "task", stringField(input.Task))
	return resp, err
}

func (c *ecsClient) RunTask(ctx context.Context, input *ecs.RunTaskInput) (*ecs.RunTaskOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.RunTask(input)
	done(err, "RunTask", "taskDefinition", stringField(input.TaskDefinition))
	return resp, err
}

func stringField(v *string) string {
	if v != nil {
		return *v
	}

	return "<nil>"
}

func intField(v *int64) string {
	if v != nil {
		return fmt.Sprintf("%d", *v)
	}

	return "<nil>"
}

// limitedClient is an ECS client that will handle limits in certain service
// calls.
type limitedClient struct {
	ECS
}

const describeServicesLimit = 10

// TODO: Parallelize this.
func (c *limitedClient) DescribeServices(ctx context.Context, input *ecs.DescribeServicesInput) (*ecs.DescribeServicesOutput, error) {
	var (
		arns     = input.Services
		max      = len(arns)
		services []*ecs.Service
	)

	// Slice off chunks of 10 arns.
	for i := 0; true; i += describeServicesLimit {
		// End point for this chunk.
		e := i + describeServicesLimit
		if e >= max {
			e = max
		}

		chunk := arns[i:e]

		resp, err := c.ECS.DescribeServices(ctx, &ecs.DescribeServicesInput{
			Cluster:  input.Cluster,
			Services: chunk,
		})
		if err != nil {
			return nil, err
		}

		services = append(services, resp.Services...)

		// No more chunks.
		if max == e {
			break
		}
	}

	return &ecs.DescribeServicesOutput{
		Services: services,
	}, nil
}

const describeTasksLimit = 100

func (c *limitedClient) DescribeTasks(ctx context.Context, input *ecs.DescribeTasksInput) (*ecs.DescribeTasksOutput, error) {
	var (
		arns  = input.Tasks
		max   = len(arns)
		tasks []*ecs.Task
	)

	// Slice off chunks of 100 arns.
	for i := 0; true; i += describeTasksLimit {
		// End point for this chunk.
		e := i + describeTasksLimit
		if e >= max {
			e = max
		}

		chunk := arns[i:e]

		resp, err := c.ECS.DescribeTasks(ctx, &ecs.DescribeTasksInput{
			Cluster: input.Cluster,
			Tasks:   chunk,
		})
		if err != nil {
			return nil, err
		}

		tasks = append(tasks, resp.Tasks...)

		// No more chunks.
		if max == e {
			break
		}
	}

	return &ecs.DescribeTasksOutput{
		Tasks: tasks,
	}, nil
}

var validLogDrivers = map[string]bool{
	"json-file": true,
	"syslog":    true,
}

func NewLogConfiguration(logDriver string, logOpts []string) *ecs.LogConfiguration {
	if logDriver == "" {
		// Default to the docker daemon default logging driver.
		return nil
	}

	logOptions := make(map[string]*string)

	for _, opt := range logOpts {
		logOpt := strings.SplitN(opt, "=", 2)
		if len(logOpt) == 2 {
			logOptions[logOpt[0]] = &logOpt[1]
		}
	}

	_, ok := validLogDrivers[logDriver]
	if !ok {
		logDriver = "json-file"
	}

	return &ecs.LogConfiguration{
		LogDriver: aws.String(logDriver),
		Options:   logOptions,
	}
}
<commit_msg>Allow users to send any log-driver to Docker/ECS<commit_after>package ecsutil

import (
	"fmt"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/remind101/pkg/trace"
	"golang.org/x/net/context"
)

// ECS represents our ECS client interface.
type ECS interface {
	// Task Definitions
	RegisterTaskDefinition(context.Context, *ecs.RegisterTaskDefinitionInput) (*ecs.RegisterTaskDefinitionOutput, error)
	DescribeTaskDefinition(context.Context, *ecs.DescribeTaskDefinitionInput) (*ecs.DescribeTaskDefinitionOutput, error)

	// Services
	CreateService(context.Context, *ecs.CreateServiceInput) (*ecs.CreateServiceOutput, error)
	DeleteService(context.Context, *ecs.DeleteServiceInput) (*ecs.DeleteServiceOutput, error)
	UpdateService(context.Context, *ecs.UpdateServiceInput) (*ecs.UpdateServiceOutput, error)
	ListServicesPages(context.Context, *ecs.ListServicesInput, func(*ecs.ListServicesOutput, bool) bool) error
	DescribeServices(context.Context, *ecs.DescribeServicesInput) (*ecs.DescribeServicesOutput, error)

	// Tasks
	ListTasksPages(context.Context, *ecs.ListTasksInput, func(*ecs.ListTasksOutput, bool) bool) error
	StopTask(context.Context, *ecs.StopTaskInput) (*ecs.StopTaskOutput, error)
	DescribeTasks(context.Context, *ecs.DescribeTasksInput) (*ecs.DescribeTasksOutput, error)
	RunTask(context.Context, *ecs.RunTaskInput) (*ecs.RunTaskOutput, error)
}

// newECSClient builds a new ECS client with autopagination and tracing.
func newECSClient(p client.ConfigProvider) ECS {
	ecs := ecs.New(p)
	return &limitedClient{
		ECS: &ecsClient{ECS: ecs},
	}
}

// ecsClient is a base ECS client implementation
type ecsClient struct {
	*ecs.ECS

	// a timer used to throttle the RegisterTaskDefinition calls. ECS only
	// allows 60/min. See http://docs.aws.amazon.com/AmazonECS/latest/developerguide/service_limits.html
	tdThrottle *time.Ticker
}

func (c *ecsClient) CreateService(ctx context.Context, input *ecs.CreateServiceInput) (*ecs.CreateServiceOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.CreateService(input)
	done(err, "CreateService", "service-name", stringField(input.ServiceName), "desired-count", intField(input.DesiredCount), "task-definition", stringField(input.TaskDefinition))
	return resp, err
}

func (c *ecsClient) DeleteService(ctx context.Context, input *ecs.DeleteServiceInput) (*ecs.DeleteServiceOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.DeleteService(input)
	done(err, "DeleteService", "service-name", stringField(input.Service))
	return resp, err
}

func (c *ecsClient) UpdateService(ctx context.Context, input *ecs.UpdateServiceInput) (*ecs.UpdateServiceOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.UpdateService(input)
	done(err, "UpdateService", "service-name", stringField(input.Service), "desired-count", intField(input.DesiredCount), "task-definition", stringField(input.TaskDefinition))
	return resp, err
}

func (c *ecsClient) RegisterTaskDefinition(ctx context.Context, input *ecs.RegisterTaskDefinitionInput) (*ecs.RegisterTaskDefinitionOutput, error) {
	if c.tdThrottle == nil {
		// Only allow 1 task definition per second.
		c.tdThrottle = time.NewTicker(time.Second)
	}

	<-c.tdThrottle.C

	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.RegisterTaskDefinition(input)
	done(err, "RegisterTaskDefinition", "family", stringField(input.Family))
	return resp, err
}

func (c *ecsClient) DescribeTaskDefinition(ctx context.Context, input *ecs.DescribeTaskDefinitionInput) (*ecs.DescribeTaskDefinitionOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.DescribeTaskDefinition(input)
	done(err, "DescribeTaskDefinition", "task-definition", stringField(input.TaskDefinition))
	return resp, err
}

func (c *ecsClient) ListServicesPages(ctx context.Context, input *ecs.ListServicesInput, fn func(*ecs.ListServicesOutput, bool) bool) error {
	ctx, done := trace.Trace(ctx)
	err := c.ECS.ListServicesPages(input, fn)
	done(err, "ListServicesPages")
	return err
}

func (c *ecsClient) DescribeServices(ctx context.Context, input *ecs.DescribeServicesInput) (*ecs.DescribeServicesOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.DescribeServices(input)
	done(err, "DescribeServices", "services", len(input.Services))
	return resp, err
}

func (c *ecsClient) ListTasksPages(ctx context.Context, input *ecs.ListTasksInput, fn func(*ecs.ListTasksOutput, bool) bool) error {
	ctx, done := trace.Trace(ctx)
	err := c.ECS.ListTasksPages(input, fn)
	done(err, "ListTasksPages")
	return err
}

func (c *ecsClient) DescribeTasks(ctx context.Context, input *ecs.DescribeTasksInput) (*ecs.DescribeTasksOutput, error) {
	return c.describeTasks(ctx, input)
}

func (c *ecsClient) describeTasks(ctx context.Context, input *ecs.DescribeTasksInput) (*ecs.DescribeTasksOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.DescribeTasks(input)
	done(err, "DescribeTasks", "tasks", len(input.Tasks))
	return resp, err
}

func (c *ecsClient) StopTask(ctx context.Context, input *ecs.StopTaskInput) (*ecs.StopTaskOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.StopTask(input)
	done(err, "StopTask", "task", stringField(input.Task))
	return resp, err
}

func (c *ecsClient) RunTask(ctx context.Context, input *ecs.RunTaskInput) (*ecs.RunTaskOutput, error) {
	ctx, done := trace.Trace(ctx)
	resp, err := c.ECS.RunTask(input)
	done(err, "RunTask", "taskDefinition", stringField(input.TaskDefinition))
	return resp, err
}

func stringField(v *string) string {
	if v != nil {
		return *v
	}

	return "<nil>"
}

func intField(v *int64) string {
	if v != nil {
		return fmt.Sprintf("%d", *v)
	}

	return "<nil>"
}

// limitedClient is an ECS client that will handle limits in certain service
// calls.
type limitedClient struct {
	ECS
}

const describeServicesLimit = 10

// TODO: Parallelize this.
func (c *limitedClient) DescribeServices(ctx context.Context, input *ecs.DescribeServicesInput) (*ecs.DescribeServicesOutput, error) {
	var (
		arns     = input.Services
		max      = len(arns)
		services []*ecs.Service
	)

	// Slice off chunks of 10 arns.
	for i := 0; true; i += describeServicesLimit {
		// End point for this chunk.
		e := i + describeServicesLimit
		if e >= max {
			e = max
		}

		chunk := arns[i:e]

		resp, err := c.ECS.DescribeServices(ctx, &ecs.DescribeServicesInput{
			Cluster:  input.Cluster,
			Services: chunk,
		})
		if err != nil {
			return nil, err
		}

		services = append(services, resp.Services...)

		// No more chunks.
		if max == e {
			break
		}
	}

	return &ecs.DescribeServicesOutput{
		Services: services,
	}, nil
}

const describeTasksLimit = 100

func (c *limitedClient) DescribeTasks(ctx context.Context, input *ecs.DescribeTasksInput) (*ecs.DescribeTasksOutput, error) {
	var (
		arns  = input.Tasks
		max   = len(arns)
		tasks []*ecs.Task
	)

	// Slice off chunks of 100 arns.
	for i := 0; true; i += describeTasksLimit {
		// End point for this chunk.
		e := i + describeTasksLimit
		if e >= max {
			e = max
		}

		chunk := arns[i:e]

		resp, err := c.ECS.DescribeTasks(ctx, &ecs.DescribeTasksInput{
			Cluster: input.Cluster,
			Tasks:   chunk,
		})
		if err != nil {
			return nil, err
		}

		tasks = append(tasks, resp.Tasks...)

		// No more chunks.
		if max == e {
			break
		}
	}

	return &ecs.DescribeTasksOutput{
		Tasks: tasks,
	}, nil
}

func NewLogConfiguration(logDriver string, logOpts []string) *ecs.LogConfiguration {
	if logDriver == "" {
		// Default to the docker daemon default logging driver.
		return nil
	}

	logOptions := make(map[string]*string)

	for _, opt := range logOpts {
		logOpt := strings.SplitN(opt, "=", 2)
		if len(logOpt) == 2 {
			logOptions[logOpt[0]] = &logOpt[1]
		}
	}

	return &ecs.LogConfiguration{
		LogDriver: aws.String(logDriver),
		Options:   logOptions,
	}
}
<|endoftext|>


<commit_before>package jwt

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/jwt/provider"
	"github.com/hellofresh/janus/pkg/render"
	"github.com/pkg/errors"
	"golang.org/x/oauth2"
)

const (
	bearer = "bearer"
)

// Handler struct
type Handler struct {
	Guard Guard
}

// Login can be used by clients to get a jwt token.
// Payload needs to be json in the form of {"username": "<USERNAME>", "password": "<PASSWORD>"}.
// Reply will be of the form {"token": "<TOKEN>"}.
func (j *Handler) Login(config config.Credentials) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		accessToken, err := extractAccessToken(r)
		if err != nil {
			render.JSON(w, http.StatusBadRequest, "failed to extract access token")
			return
		}

		httpClient := getClient(accessToken)
		factory := provider.Factory{}
		p := factory.Build(r.URL.Query().Get("provider"), config)

		verified, err := p.Verify(r, httpClient)
		if err != nil {
			render.JSON(w, http.StatusBadRequest, err.Error())
			return
		}

		if !verified {
			render.JSON(w, http.StatusUnauthorized, "verification failed")
			return
		}

		if 0 == j.Guard.Timeout {
			j.Guard.Timeout = time.Hour
		}

		claims, err := p.GetClaims(httpClient)
		if err != nil {
			render.JSON(w, http.StatusBadRequest, err.Error())
			return
		}

		token, err := IssueAdminToken(j.Guard.SigningMethod, claims, j.Guard.Timeout)
		if err != nil {
			render.JSON(w, http.StatusUnauthorized, "problem issuing JWT")
			return
		}

		render.JSON(w, http.StatusOK, token)
	}
}

// Refresh can be used to refresh existing and valid jwt token.
// Reply will be of the form {"token": "<TOKEN>", "expire": "<DateTime in RFC-3339 format>"}.
func (j *Handler) Refresh() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		parser := Parser{j.Guard.ParserConfig}
		token, _ := parser.ParseFromRequest(r)
		claims := token.Claims.(jwt.MapClaims)

		origIat := int64(claims["iat"].(float64))

		if origIat < time.Now().Add(-j.Guard.MaxRefresh).Unix() {
			render.JSON(w, http.StatusUnauthorized, "token is expired")
			return
		}

		// Create the token
		newToken := jwt.New(jwt.GetSigningMethod(j.Guard.SigningMethod.Alg))
		newClaims := newToken.Claims.(jwt.MapClaims)

		for key := range claims {
			newClaims[key] = claims[key]
		}

		expire := time.Now().Add(j.Guard.Timeout)
		newClaims["sub"] = claims["sub"]
		newClaims["exp"] = expire.Unix()
		newClaims["iat"] = origIat

		// currently only HSXXX algorithms are supported for issuing admin token, so we cast key to bytes array
		tokenString, err := newToken.SignedString([]byte(j.Guard.SigningMethod.Key))
		if err != nil {
			render.JSON(w, http.StatusUnauthorized, "create JWT Token failed")
			return
		}

		render.JSON(w, http.StatusOK, render.M{
			"token":  tokenString,
			"type":   "Bearer",
			"expire": expire.Format(time.RFC3339),
		})
	}
}

func extractAccessToken(r *http.Request) (string, error) {
	// We're using OAuth, start checking for access keys
	authHeaderValue := r.Header.Get("Authorization")
	parts := strings.Split(authHeaderValue, " ")
	if len(parts) < 2 {
		return "", errors.New("attempted access with malformed header, no auth header found")
	}

	if strings.ToLower(parts[0]) != bearer {
		return "", errors.New("bearer token malformed")
	}

	return parts[1], nil
}

func getClient(token string) *http.Client {
	ctx := context.Background()
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: token},
	)
	return oauth2.NewClient(ctx, ts)
}
<commit_msg>Ignores when an invalid access token is send at thsi stage<commit_after>package jwt

import (
	"context"
	"net/http"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/hellofresh/janus/pkg/config"
	"github.com/hellofresh/janus/pkg/jwt/provider"
	"github.com/hellofresh/janus/pkg/render"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"golang.org/x/oauth2"
)

const (
	bearer = "bearer"
)

// Handler struct
type Handler struct {
	Guard Guard
}

// Login can be used by clients to get a jwt token.
// Payload needs to be json in the form of {"username": "<USERNAME>", "password": "<PASSWORD>"}.
// Reply will be of the form {"token": "<TOKEN>"}.
func (j *Handler) Login(config config.Credentials) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		accessToken, err := extractAccessToken(r)
		if err != nil {
			log.WithError(err).Debug("failed to extract access token")
		}

		httpClient := getClient(accessToken)
		factory := provider.Factory{}
		p := factory.Build(r.URL.Query().Get("provider"), config)

		verified, err := p.Verify(r, httpClient)
		if err != nil {
			render.JSON(w, http.StatusBadRequest, err.Error())
			return
		}

		if !verified {
			render.JSON(w, http.StatusUnauthorized, "verification failed")
			return
		}

		if 0 == j.Guard.Timeout {
			j.Guard.Timeout = time.Hour
		}

		claims, err := p.GetClaims(httpClient)
		if err != nil {
			render.JSON(w, http.StatusBadRequest, err.Error())
			return
		}

		token, err := IssueAdminToken(j.Guard.SigningMethod, claims, j.Guard.Timeout)
		if err != nil {
			render.JSON(w, http.StatusUnauthorized, "problem issuing JWT")
			return
		}

		render.JSON(w, http.StatusOK, token)
	}
}

// Refresh can be used to refresh existing and valid jwt token.
// Reply will be of the form {"token": "<TOKEN>", "expire": "<DateTime in RFC-3339 format>"}.
func (j *Handler) Refresh() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		parser := Parser{j.Guard.ParserConfig}
		token, _ := parser.ParseFromRequest(r)
		claims := token.Claims.(jwt.MapClaims)

		origIat := int64(claims["iat"].(float64))

		if origIat < time.Now().Add(-j.Guard.MaxRefresh).Unix() {
			render.JSON(w, http.StatusUnauthorized, "token is expired")
			return
		}

		// Create the token
		newToken := jwt.New(jwt.GetSigningMethod(j.Guard.SigningMethod.Alg))
		newClaims := newToken.Claims.(jwt.MapClaims)

		for key := range claims {
			newClaims[key] = claims[key]
		}

		expire := time.Now().Add(j.Guard.Timeout)
		newClaims["sub"] = claims["sub"]
		newClaims["exp"] = expire.Unix()
		newClaims["iat"] = origIat

		// currently only HSXXX algorithms are supported for issuing admin token, so we cast key to bytes array
		tokenString, err := newToken.SignedString([]byte(j.Guard.SigningMethod.Key))
		if err != nil {
			render.JSON(w, http.StatusUnauthorized, "create JWT Token failed")
			return
		}

		render.JSON(w, http.StatusOK, render.M{
			"token":  tokenString,
			"type":   "Bearer",
			"expire": expire.Format(time.RFC3339),
		})
	}
}

func extractAccessToken(r *http.Request) (string, error) {
	// We're using OAuth, start checking for access keys
	authHeaderValue := r.Header.Get("Authorization")
	parts := strings.Split(authHeaderValue, " ")
	if len(parts) < 2 {
		return "", errors.New("attempted access with malformed header, no auth header found")
	}

	if strings.ToLower(parts[0]) != bearer {
		return "", errors.New("bearer token malformed")
	}

	return parts[1], nil
}

func getClient(token string) *http.Client {
	ctx := context.Background()
	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: token},
	)
	return oauth2.NewClient(ctx, ts)
}
<|endoftext|>


<commit_before>package kubectl

// Get returns Kubernetes resources
func (r RealRunner) Get(stdin []byte, ns string) ([]byte, error) {
	args := []string{"get", "-f", "-"}

	if ns != "" {
		args = append([]string{"--namespace=" + ns}, args...)
	}
	cmd := command(args...)
	assignStdin(cmd, stdin)

	return cmd.CombinedOutput()
}

// GetByKind gets a named thing by kind.
func (r RealRunner) GetByKind(kind, name, ns string) (string, error) {
	args := []string{"get", kind}

	if name != "" {
		args = append([]string{name}, args...)
	}

	if ns != "" {
		args = append([]string{"--namespace=" + ns}, args...)
	}
	cmd := command(args...)
	o, err := cmd.CombinedOutput()
	return string(o), err
}

// Get returns the commands to kubectl
func (r PrintRunner) Get(stdin []byte, ns string) ([]byte, error) {
	args := []string{"get", "-f", "-"}

	if ns != "" {
		args = append([]string{"--namespace=" + ns}, args...)
	}
	cmd := command(args...)
	assignStdin(cmd, stdin)

	return []byte(cmd.String()), nil
}

// GetByKind gets a named thing by kind.
func (r PrintRunner) GetByKind(kind, name, ns string) (string, error) {
	args := []string{"get", kind}

	if name != "" {
		args = append([]string{name}, args...)
	}

	if ns != "" {
		args = append([]string{"--namespace=" + ns}, args...)
	}
	cmd := command(args...)
	return cmd.String(), nil
}
<commit_msg>ref(kubectl): update GetByKind func description<commit_after>package kubectl

// Get returns Kubernetes resources
func (r RealRunner) Get(stdin []byte, ns string) ([]byte, error) {
	args := []string{"get", "-f", "-"}

	if ns != "" {
		args = append([]string{"--namespace=" + ns}, args...)
	}
	cmd := command(args...)
	assignStdin(cmd, stdin)

	return cmd.CombinedOutput()
}

// GetByKind gets resources by kind, name(optional), and namespace(optional)
func (r RealRunner) GetByKind(kind, name, ns string) (string, error) {
	args := []string{"get", kind}

	if name != "" {
		args = append([]string{name}, args...)
	}

	if ns != "" {
		args = append([]string{"--namespace=" + ns}, args...)
	}
	cmd := command(args...)
	o, err := cmd.CombinedOutput()
	return string(o), err
}

// Get returns the commands to kubectl
func (r PrintRunner) Get(stdin []byte, ns string) ([]byte, error) {
	args := []string{"get", "-f", "-"}

	if ns != "" {
		args = append([]string{"--namespace=" + ns}, args...)
	}
	cmd := command(args...)
	assignStdin(cmd, stdin)

	return []byte(cmd.String()), nil
}

// GetByKind gets resources by kind, name(optional), and namespace(optional)
func (r PrintRunner) GetByKind(kind, name, ns string) (string, error) {
	args := []string{"get", kind}

	if name != "" {
		args = append([]string{name}, args...)
	}

	if ns != "" {
		args = append([]string{"--namespace=" + ns}, args...)
	}
	cmd := command(args...)
	return cmd.String(), nil
}
<|endoftext|>


<commit_before>package modules

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/pajlada/botsync/pkg/client"
	"github.com/pajlada/botsync/pkg/protocol"
	"github.com/pajlada/pajbot2/pkg"
)

func init() {
	Register(afkSpec)
}

var (
	afkDatabase = map[string]bool{}
)

type afk struct {
	botChannel pkg.BotChannel

	server *server

	commands map[string]pkg.CustomCommand

	botsync *client.Client
}

var afkSpec = &moduleSpec{
	id:               "afk",
	name:             "AFK",
	maker:            newAFK,
	enabledByDefault: false,
}

func newAFK() pkg.Module {
	m := &afk{
		server: &_server,

		commands: make(map[string]pkg.CustomCommand),

		botsync: client.NewClient("ws://localhost:8080/ws/pubsub"),
	}

	m.botsync.OnMessage(m.onMessage)

	m.botsync.OnConnect(func() {
		fmt.Println("Connected to botsync")
		m.botsync.Send(protocol.NewAFKSubscribeMessage(m.botChannel.ChannelID()))
		m.botsync.Send(protocol.NewBackSubscribeMessage(m.botChannel.ChannelID()))
	})

	return m
}

type afkCmd struct {
	m *afk
}

func (c *afkCmd) Trigger(botChannel pkg.BotChannel, parts []string, channel pkg.Channel, user pkg.User, message pkg.Message, action pkg.Action) {
	if user.IsModerator() || user.IsBroadcaster(channel) {
		c.m.botsync.Send(protocol.NewAFKMessage(&protocol.AFKParameters{
			UserID:   user.GetID(),
			UserName: user.GetName(),

			Reason: strings.Join(parts[1:], " "),

			ChannelID:   botChannel.ChannelID(),
			ChannelName: botChannel.ChannelName(),
		}))
	}
}

func (m *afk) registerCommand(aliases []string, command pkg.CustomCommand) {
	for _, alias := range aliases {
		m.commands[alias] = command
	}
}

func (m *afk) Initialize(botChannel pkg.BotChannel, settings []byte) error {
	m.botChannel = botChannel

	m.registerCommand([]string{"!afk", "!gn"}, &afkCmd{m})

	m.botsync.SetAuthentication(protocol.Authentication{
		TwitchUserID:        botChannel.Bot().TwitchAccount().ID(),
		AuthenticationToken: "penis",
	})

	go func() {
		for {
			err := m.botsync.Connect()
			if err != nil {
				if err == client.ErrDisconnected {
					return
				}
			}

			// wait a second before attempting to reconnect
			<-time.After(time.Second)
		}
	}()

	return nil
}

func (m *afk) onMessage(message *protocol.Message) {
	switch message.Topic {
	case "afk":
		parameters := protocol.AFKParameters{}
		err := json.Unmarshal(message.Data, &parameters)
		if err != nil {
			fmt.Println("Error parsing afk message:", err)
			return
		}
		m.botChannel.Say(parameters.UserName + " just went afk: " + parameters.Reason)
		afkDatabase[parameters.UserID] = true

	case "back":
		parameters := protocol.BackParameters{}
		err := json.Unmarshal(message.Data, &parameters)
		if err != nil {
			fmt.Println("Error parsing afk message:", err)
			return
		}
		m.botChannel.Say(parameters.UserName + " just went came back from being afk (" + parameters.AFKChannelName + "): " + parameters.Reason)
		delete(afkDatabase, parameters.UserID)
	}
}

func (m *afk) Disable() error {
	m.botsync.Disconnect()
	return nil
}

func (m *afk) Spec() pkg.ModuleSpec {
	return afkSpec
}

func (m *afk) BotChannel() pkg.BotChannel {
	return m.botChannel
}

func (m *afk) OnWhisper(bot pkg.BotChannel, source pkg.User, message pkg.Message) error {
	return nil
}

func (m *afk) OnMessage(bot pkg.BotChannel, user pkg.User, message pkg.Message, action pkg.Action) error {
	if _, ok := afkDatabase[user.GetID()]; ok {
		m.botsync.Send(protocol.NewBackMessage(&protocol.BackParameters{
			UserID:   user.GetID(),
			UserName: user.GetName(),

			ChannelID:   bot.ChannelID(),
			ChannelName: bot.ChannelName(),
		}))
		return nil
	}

	parts := strings.Split(message.GetText(), " ")
	if len(parts) == 0 {
		return nil
	}

	if command, ok := m.commands[strings.ToLower(parts[0])]; ok {
		command.Trigger(m.botChannel, parts, bot.Channel(), user, message, action)
	}

	return nil
}
<commit_msg>more work on afk module<commit_after>package modules

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/pajlada/botsync/pkg/client"
	"github.com/pajlada/botsync/pkg/protocol"
	"github.com/pajlada/pajbot2/pkg"
	"github.com/pajlada/pajbot2/pkg/utils"
)

func init() {
	Register(afkSpec)
}

var (
	afkDatabase = map[string]bool{}
)

type afk struct {
	botChannel pkg.BotChannel

	server *server

	commands map[string]pkg.CustomCommand

	botsync *client.Client
}

var afkSpec = &moduleSpec{
	id:               "afk",
	name:             "AFK",
	maker:            newAFK,
	enabledByDefault: false,
}

func newAFK() pkg.Module {
	m := &afk{
		server: &_server,

		commands: make(map[string]pkg.CustomCommand),

		botsync: client.NewClient("ws://localhost:8080/ws/pubsub"),
	}

	m.botsync.OnMessage(m.onMessage)

	m.botsync.OnConnect(func() {
		fmt.Println("Connected to botsync")
		m.botsync.Send(protocol.NewAFKSubscribeMessage(m.botChannel.ChannelID()))
		m.botsync.Send(protocol.NewBackSubscribeMessage(m.botChannel.ChannelID()))
	})

	return m
}

type afkCmd struct {
	m *afk
}

func (c *afkCmd) Trigger(botChannel pkg.BotChannel, parts []string, channel pkg.Channel, user pkg.User, message pkg.Message, action pkg.Action) {
	if user.IsModerator() || user.IsBroadcaster(channel) {
		c.m.botsync.Send(protocol.NewAFKMessage(&protocol.AFKParameters{
			UserID:   user.GetID(),
			UserName: user.GetName(),

			Reason: strings.Join(parts[1:], " "),

			ChannelID:   botChannel.ChannelID(),
			ChannelName: botChannel.ChannelName(),
		}))
	}
}

func (m *afk) registerCommand(aliases []string, command pkg.CustomCommand) {
	for _, alias := range aliases {
		m.commands[alias] = command
	}
}

func (m *afk) Initialize(botChannel pkg.BotChannel, settings []byte) error {
	m.botChannel = botChannel

	m.registerCommand([]string{"!afk", "!gn"}, &afkCmd{m})

	m.botsync.SetAuthentication(protocol.Authentication{
		TwitchUserID:        botChannel.Bot().TwitchAccount().ID(),
		AuthenticationToken: "penis",
	})

	go func() {
		for {
			err := m.botsync.Connect()
			if err != nil {
				if err == client.ErrDisconnected {
					return
				}
			}

			// wait a second before attempting to reconnect
			<-time.After(time.Second)
		}
	}()

	return nil
}

func (m *afk) onMessage(message *protocol.Message) {
	switch message.Topic {
	case "afk":
		parameters := protocol.AFKParameters{}
		err := json.Unmarshal(message.Data, &parameters)
		if err != nil {
			fmt.Println("Error parsing afk message:", err)
			return
		}
		if !message.Historic {
			m.botChannel.Say(parameters.UserName + " just went afk: " + parameters.Reason)
		}
		afkDatabase[parameters.UserID] = true

	case "back":
		parameters := protocol.BackParameters{}
		err := json.Unmarshal(message.Data, &parameters)
		if err != nil {
			fmt.Println("Error parsing afk message:", err)
			return
		}
		afkDuration := time.Millisecond * time.Duration(parameters.Duration)
		response := fmt.Sprintf("%s just came back after %s: %s",
			parameters.UserName, utils.DurationString(afkDuration), parameters.Reason)
		m.botChannel.Say(response)
		delete(afkDatabase, parameters.UserID)
	}
}

func (m *afk) Disable() error {
	m.botsync.Disconnect()
	return nil
}

func (m *afk) Spec() pkg.ModuleSpec {
	return afkSpec
}

func (m *afk) BotChannel() pkg.BotChannel {
	return m.botChannel
}

func (m *afk) OnWhisper(bot pkg.BotChannel, source pkg.User, message pkg.Message) error {
	return nil
}

func (m *afk) OnMessage(bot pkg.BotChannel, user pkg.User, message pkg.Message, action pkg.Action) error {
	if _, ok := afkDatabase[user.GetID()]; ok {
		m.botsync.Send(protocol.NewBackMessage(&protocol.BackParameters{
			UserID:   user.GetID(),
			UserName: user.GetName(),

			ChannelID:   bot.ChannelID(),
			ChannelName: bot.ChannelName(),
		}))
		return nil
	}

	parts := strings.Split(message.GetText(), " ")
	if len(parts) == 0 {
		return nil
	}

	if command, ok := m.commands[strings.ToLower(parts[0])]; ok {
		command.Trigger(m.botChannel, parts, bot.Channel(), user, message, action)
	}

	return nil
}
<|endoftext|>