<commit_before>package disgo

// Warning: This file has been automatically generated by generate/eventmethods/main.go
// Do NOT make changes here, instead adapt events.go and run go generate

import (
	"encoding/json"
	"github.com/slf4go/logger"
)

func allocateEvent(eventName string) *Event {
	var event Event

	// Because encoding/json doesn't initialise embbeded struct pointers properly, we'll also initialise them here
	switch eventName {
	case "CHANNEL_CREATE":
		event = &ChannelCreateEvent{Channel: &Channel{}}
	case "CHANNEL_DELETE":
		event = &ChannelDeleteEvent{Channel: &Channel{}}
	case "CHANNEL_UPDATE":
		event = &ChannelUpdateEvent{Channel: &Channel{}}
	case "GUILD_BAN_ADD":
		event = &GuildBanAddEvent{User: &User{}}
	case "GUILD_BAN_REMOVE":
		event = &GuildBanRemoveEvent{User: &User{}}
	case "GUILD_CREATE":
		event = &GuildCreateEvent{Guild: &Guild{}}
	case "GUILD_DELETE":
		event = &GuildDeleteEvent{Guild: &Guild{}}
	case "GUILD_EMOJIS_UPDATE":
		event = &GuildEmojisUpdateEvent{}
	case "GUILD_INTEGRATIONS_UPDATE":
		event = &GuildIntegrationsUpdateEvent{}
	case "GUILD_MEMBER_ADD":
		event = &GuildMemberAddEvent{GuildMember: &GuildMember{}}
	case "GUILD_MEMBER_REMOVE":
		event = &GuildMemberRemoveEvent{}
	case "GUILD_MEMBER_UPDATE":
		event = &GuildMemberUpdateEvent{}
	case "GUILD_MEMBERS_CHUNK":
		event = &GuildMembersChunkEvent{}
	case "GUILD_ROLE_CREATE":
		event = &GuildRoleCreateEvent{}
	case "GUILD_ROLE_DELETE":
		event = &GuildRoleDeleteEvent{}
	case "GUILD_ROLE_UPDATE":
		event = &GuildRoleUpdateEvent{}
	case "GUILD_UPDATE":
		event = &GuildUpdateEvent{Guild: &Guild{}}
	case "MESSAGE_CREATE":
		event = &MessageCreateEvent{Message: &Message{}}
	case "MESSAGE_DELETE_BULK":
		event = &MessageDeleteBulkEvent{}
	case "MESSAGE_DELETE":
		event = &MessageDeleteEvent{}
	case "MESSAGE_REACTION_ADD":
		event = &MessageReactionAddEvent{}
	case "MESSAGE_REACTION_REMOVE":
		event = &MessageReactionRemoveEvent{}
	case "MESSAGE_UPDATE":
		event = &MessageUpdateEvent{Message: &Message{}}
	case "PRESENCE_UPDATE":
		event = &PresenceUpdateEvent{Presence: &Presence{}}
	case "READY":
		event = &ReadyEvent{}
	case "RESUMED":
		event = &ResumedEvent{}
	case "TYPING_START":
		event = &TypingStartEvent{}
	case "USER_UPDATE":
		event = &UserUpdateEvent{User: &User{}}
	default:
		logger.Errorf("Event with name '%s' was dispatched by Discord, but we don't know this event. (DisGo outdated?)", eventName)
		return nil
	}

	return &event
}

func (*ChannelCreateEvent) eventName() string {
	return "CHANNEL_CREATE"
}

func (e *ChannelCreateEvent) setSession(s *Session) {
	e.Channel.session = s
}

func (*ChannelDeleteEvent) eventName() string {
	return "CHANNEL_DELETE"
}

func (e *ChannelDeleteEvent) setSession(s *Session) {
	e.Channel.session = s
}

func (*ChannelUpdateEvent) eventName() string {
	return "CHANNEL_UPDATE"
}

func (e *ChannelUpdateEvent) setSession(s *Session) {
	e.Channel.session = s
}

func (*GuildBanAddEvent) eventName() string {
	return "GUILD_BAN_ADD"
}

func (e *GuildBanAddEvent) setSession(s *Session) {
	e.User.session = s
}

func (*GuildBanRemoveEvent) eventName() string {
	return "GUILD_BAN_REMOVE"
}

func (e *GuildBanRemoveEvent) setSession(s *Session) {
	e.User.session = s
}

func (*GuildCreateEvent) eventName() string {
	return "GUILD_CREATE"
}

func (e *GuildCreateEvent) setSession(s *Session) {
	e.Guild.session = s
}

func (*GuildDeleteEvent) eventName() string {
	return "GUILD_DELETE"
}

func (e *GuildDeleteEvent) setSession(s *Session) {
	e.Guild.session = s
}

func (*GuildEmojisUpdateEvent) eventName() string {
	return "GUILD_EMOJIS_UPDATE"
}

func (e *GuildEmojisUpdateEvent) setSession(s *Session) {
}

func (*GuildIntegrationsUpdateEvent) eventName() string {
	return "GUILD_INTEGRATIONS_UPDATE"
}

func (e *GuildIntegrationsUpdateEvent) setSession(s *Session) {
}

func (*GuildMemberAddEvent) eventName() string {
	return "GUILD_MEMBER_ADD"
}

func (e *GuildMemberAddEvent) setSession(s *Session) {
	e.GuildMember.session = s
}

func (*GuildMemberRemoveEvent) eventName() string {
	return "GUILD_MEMBER_REMOVE"
}

func (e *GuildMemberRemoveEvent) setSession(s *Session) {
	e.User.session = s
}

func (*GuildMemberUpdateEvent) eventName() string {
	return "GUILD_MEMBER_UPDATE"
}

func (e *GuildMemberUpdateEvent) setSession(s *Session) {
	e.User.session = s
}

func (*GuildMembersChunkEvent) eventName() string {
	return "GUILD_MEMBERS_CHUNK"
}

func (e *GuildMembersChunkEvent) setSession(s *Session) {
}

func (*GuildRoleCreateEvent) eventName() string {
	return "GUILD_ROLE_CREATE"
}

func (e *GuildRoleCreateEvent) setSession(s *Session) {
	e.Role.session = s
}

func (*GuildRoleDeleteEvent) eventName() string {
	return "GUILD_ROLE_DELETE"
}

func (e *GuildRoleDeleteEvent) setSession(s *Session) {
}

func (*GuildRoleUpdateEvent) eventName() string {
	return "GUILD_ROLE_UPDATE"
}

func (e *GuildRoleUpdateEvent) setSession(s *Session) {
	e.Role.session = s
}

func (*GuildUpdateEvent) eventName() string {
	return "GUILD_UPDATE"
}

func (e *GuildUpdateEvent) setSession(s *Session) {
	e.Guild.session = s
}

func (*MessageCreateEvent) eventName() string {
	return "MESSAGE_CREATE"
}

func (e *MessageCreateEvent) setSession(s *Session) {
	e.Message.session = s
}

func (*MessageDeleteBulkEvent) eventName() string {
	return "MESSAGE_DELETE_BULK"
}

func (e *MessageDeleteBulkEvent) setSession(s *Session) {
}

func (*MessageDeleteEvent) eventName() string {
	return "MESSAGE_DELETE"
}

func (e *MessageDeleteEvent) setSession(s *Session) {
}

func (*MessageReactionAddEvent) eventName() string {
	return "MESSAGE_REACTION_ADD"
}

func (e *MessageReactionAddEvent) setSession(s *Session) {
}

func (*MessageReactionRemoveEvent) eventName() string {
	return "MESSAGE_REACTION_REMOVE"
}

func (e *MessageReactionRemoveEvent) setSession(s *Session) {
}

func (*MessageUpdateEvent) eventName() string {
	return "MESSAGE_UPDATE"
}

func (e *MessageUpdateEvent) setSession(s *Session) {
	e.Message.session = s
}

func (*PresenceUpdateEvent) eventName() string {
	return "PRESENCE_UPDATE"
}

func (e *PresenceUpdateEvent) setSession(s *Session) {
}

func (*ReadyEvent) eventName() string {
	return "READY"
}

func (e *ReadyEvent) setSession(s *Session) {
	e.User.session = s
	for _, item := range e.Guilds {
		item.session = s
	}
}

func (*ResumedEvent) eventName() string {
	return "RESUMED"
}

func (e *ResumedEvent) setSession(s *Session) {
}

func (*TypingStartEvent) eventName() string {
	return "TYPING_START"
}

func (e *TypingStartEvent) setSession(s *Session) {
}

func (*UserUpdateEvent) eventName() string {
	return "USER_UPDATE"
}

func (e *UserUpdateEvent) setSession(s *Session) {
	e.User.session = s
}
<commit_msg>Regenerated events<commit_after>package disgo

// Warning: This file has been automatically generated by generate/eventmethods/main.go
// Do NOT make changes here, instead adapt events.go and run go generate

import "github.com/slf4go/logger"

func allocateEvent(eventName string) *Event {
	var event Event

	// Because encoding/json doesn't initialise embbeded struct pointers properly, we'll also initialise them here
	switch eventName {
	case "CHANNEL_CREATE":
		event = &ChannelCreateEvent{Channel: &Channel{}}
	case "CHANNEL_DELETE":
		event = &ChannelDeleteEvent{Channel: &Channel{}}
	case "CHANNEL_UPDATE":
		event = &ChannelUpdateEvent{Channel: &Channel{}}
	case "GUILD_BAN_ADD":
		event = &GuildBanAddEvent{User: &User{}}
	case "GUILD_BAN_REMOVE":
		event = &GuildBanRemoveEvent{User: &User{}}
	case "GUILD_CREATE":
		event = &GuildCreateEvent{Guild: &Guild{}}
	case "GUILD_DELETE":
		event = &GuildDeleteEvent{Guild: &Guild{}}
	case "GUILD_EMOJIS_UPDATE":
		event = &GuildEmojisUpdateEvent{}
	case "GUILD_INTEGRATIONS_UPDATE":
		event = &GuildIntegrationsUpdateEvent{}
	case "GUILD_MEMBER_ADD":
		event = &GuildMemberAddEvent{GuildMember: &GuildMember{}}
	case "GUILD_MEMBER_REMOVE":
		event = &GuildMemberRemoveEvent{}
	case "GUILD_MEMBER_UPDATE":
		event = &GuildMemberUpdateEvent{}
	case "GUILD_MEMBERS_CHUNK":
		event = &GuildMembersChunkEvent{}
	case "GUILD_ROLE_CREATE":
		event = &GuildRoleCreateEvent{}
	case "GUILD_ROLE_DELETE":
		event = &GuildRoleDeleteEvent{}
	case "GUILD_ROLE_UPDATE":
		event = &GuildRoleUpdateEvent{}
	case "GUILD_UPDATE":
		event = &GuildUpdateEvent{Guild: &Guild{}}
	case "MESSAGE_CREATE":
		event = &MessageCreateEvent{Message: &Message{}}
	case "MESSAGE_DELETE_BULK":
		event = &MessageDeleteBulkEvent{}
	case "MESSAGE_DELETE":
		event = &MessageDeleteEvent{}
	case "MESSAGE_REACTION_ADD":
		event = &MessageReactionAddEvent{}
	case "MESSAGE_REACTION_REMOVE":
		event = &MessageReactionRemoveEvent{}
	case "MESSAGE_UPDATE":
		event = &MessageUpdateEvent{Message: &Message{}}
	case "PRESENCE_UPDATE":
		event = &PresenceUpdateEvent{Presence: &Presence{}}
	case "READY":
		event = &ReadyEvent{}
	case "RESUMED":
		event = &ResumedEvent{}
	case "TYPING_START":
		event = &TypingStartEvent{}
	case "USER_UPDATE":
		event = &UserUpdateEvent{User: &User{}}
	default:
		logger.Errorf("Event with name '%s' was dispatched by Discord, but we don't know this event. (DisGo outdated?)", eventName)
		return nil
	}

	return &event
}

func (*ChannelCreateEvent) eventName() string {
	return "CHANNEL_CREATE"
}

func (e *ChannelCreateEvent) setSession(s *Session) {
	e.Channel.session = s
}

func (*ChannelDeleteEvent) eventName() string {
	return "CHANNEL_DELETE"
}

func (e *ChannelDeleteEvent) setSession(s *Session) {
	e.Channel.session = s
}

func (*ChannelUpdateEvent) eventName() string {
	return "CHANNEL_UPDATE"
}

func (e *ChannelUpdateEvent) setSession(s *Session) {
	e.Channel.session = s
}

func (*GuildBanAddEvent) eventName() string {
	return "GUILD_BAN_ADD"
}

func (e *GuildBanAddEvent) setSession(s *Session) {
	e.User.session = s
}

func (*GuildBanRemoveEvent) eventName() string {
	return "GUILD_BAN_REMOVE"
}

func (e *GuildBanRemoveEvent) setSession(s *Session) {
	e.User.session = s
}

func (*GuildCreateEvent) eventName() string {
	return "GUILD_CREATE"
}

func (e *GuildCreateEvent) setSession(s *Session) {
	e.Guild.session = s
}

func (*GuildDeleteEvent) eventName() string {
	return "GUILD_DELETE"
}

func (e *GuildDeleteEvent) setSession(s *Session) {
	e.Guild.session = s
}

func (*GuildEmojisUpdateEvent) eventName() string {
	return "GUILD_EMOJIS_UPDATE"
}

func (e *GuildEmojisUpdateEvent) setSession(s *Session) {
}

func (*GuildIntegrationsUpdateEvent) eventName() string {
	return "GUILD_INTEGRATIONS_UPDATE"
}

func (e *GuildIntegrationsUpdateEvent) setSession(s *Session) {
}

func (*GuildMemberAddEvent) eventName() string {
	return "GUILD_MEMBER_ADD"
}

func (e *GuildMemberAddEvent) setSession(s *Session) {
	e.GuildMember.session = s
}

func (*GuildMemberRemoveEvent) eventName() string {
	return "GUILD_MEMBER_REMOVE"
}

func (e *GuildMemberRemoveEvent) setSession(s *Session) {
	e.User.session = s
}

func (*GuildMemberUpdateEvent) eventName() string {
	return "GUILD_MEMBER_UPDATE"
}

func (e *GuildMemberUpdateEvent) setSession(s *Session) {
	e.User.session = s
}

func (*GuildMembersChunkEvent) eventName() string {
	return "GUILD_MEMBERS_CHUNK"
}

func (e *GuildMembersChunkEvent) setSession(s *Session) {
}

func (*GuildRoleCreateEvent) eventName() string {
	return "GUILD_ROLE_CREATE"
}

func (e *GuildRoleCreateEvent) setSession(s *Session) {
	e.Role.session = s
}

func (*GuildRoleDeleteEvent) eventName() string {
	return "GUILD_ROLE_DELETE"
}

func (e *GuildRoleDeleteEvent) setSession(s *Session) {
}

func (*GuildRoleUpdateEvent) eventName() string {
	return "GUILD_ROLE_UPDATE"
}

func (e *GuildRoleUpdateEvent) setSession(s *Session) {
	e.Role.session = s
}

func (*GuildUpdateEvent) eventName() string {
	return "GUILD_UPDATE"
}

func (e *GuildUpdateEvent) setSession(s *Session) {
	e.Guild.session = s
}

func (*MessageCreateEvent) eventName() string {
	return "MESSAGE_CREATE"
}

func (e *MessageCreateEvent) setSession(s *Session) {
	e.Message.session = s
}

func (*MessageDeleteBulkEvent) eventName() string {
	return "MESSAGE_DELETE_BULK"
}

func (e *MessageDeleteBulkEvent) setSession(s *Session) {
}

func (*MessageDeleteEvent) eventName() string {
	return "MESSAGE_DELETE"
}

func (e *MessageDeleteEvent) setSession(s *Session) {
}

func (*MessageReactionAddEvent) eventName() string {
	return "MESSAGE_REACTION_ADD"
}

func (e *MessageReactionAddEvent) setSession(s *Session) {
}

func (*MessageReactionRemoveEvent) eventName() string {
	return "MESSAGE_REACTION_REMOVE"
}

func (e *MessageReactionRemoveEvent) setSession(s *Session) {
}

func (*MessageUpdateEvent) eventName() string {
	return "MESSAGE_UPDATE"
}

func (e *MessageUpdateEvent) setSession(s *Session) {
	e.Message.session = s
}

func (*PresenceUpdateEvent) eventName() string {
	return "PRESENCE_UPDATE"
}

func (e *PresenceUpdateEvent) setSession(s *Session) {
}

func (*ReadyEvent) eventName() string {
	return "READY"
}

func (e *ReadyEvent) setSession(s *Session) {
	e.User.session = s
	for _, item := range e.Guilds {
		item.session = s
	}
}

func (*ResumedEvent) eventName() string {
	return "RESUMED"
}

func (e *ResumedEvent) setSession(s *Session) {
}

func (*TypingStartEvent) eventName() string {
	return "TYPING_START"
}

func (e *TypingStartEvent) setSession(s *Session) {
}

func (*UserUpdateEvent) eventName() string {
	return "USER_UPDATE"
}

func (e *UserUpdateEvent) setSession(s *Session) {
	e.User.session = s
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Space Monkey, 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 present

import (
	"io"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"github.com/spacemonkeygo/errors"
	"github.com/spacemonkeygo/errors/errhttp"
	"gopkg.in/spacemonkeygo/monkit.v2"
)

var (
	BadRequest = errors.NewClass("Bad Request", errhttp.SetStatusCode(400))
	NotFound   = errors.NewClass("Not Found", errhttp.SetStatusCode(404))
)

// Result writes the expected data to io.Writer and returns any errors if
// found.
type Result func(io.Writer) error

func curry(reg *monkit.Registry,
	f func(*monkit.Registry, io.Writer) error) func(io.Writer) error {
	return func(w io.Writer) error {
		return f(reg, w)
	}
}

// FromRequest takes a registry (usually the Default registry), an incoming
// path, and optional query parameters, and returns a Result if possible.
//
// FromRequest understands the following paths:
//  * /ps, /ps/text       - returns the result of SpansText
//  * /ps/dot             - returns the result of SpansDot
//  * /ps/json            - returns the result of SpansJSON
//  * /funcs, /funcs/text - returns the result of FuncsText
//  * /funcs/dot          - returns the result of FuncsDot
//  * /funcs/json         - returns the result of FuncsJSON
//  * /stats, /stats/text - returns the result of StatsText
//  * /stats/json         - returns the result of StatsJSON
//  * /trace/svg          - returns the result of TraceQuerySVG
//  * /trace/json         - returns the result of TraceQueryJSON
//
// The last two paths are worth discussing in more detail, as they take
// query parameters. All trace endpoints require at least one of the following
// two query parameters:
//  * regex    - If provided, the very next Span that crosses a Func that has
//               a name that matches this regex will start a trace until that
//               triggering Span ends, provided the trace_id matches.
//  * trace_id - If provided, the very next Span on a trace with the given
//               trace id will start a trace until the triggering Span ends,
//               provided the regex matches. NOTE: the trace_id will be parsed
//               in hex.
// By default, regular expressions are matched ahead of time against all known
// Funcs, but perhaps the Func you want to trace hasn't been observed by the
// process yet, in which case the regex will fail to match anything. You can
// turn off this preselection behavior by providing preselect=false as an
// additional query param. Be advised that until a trace completes, whether
// or not it has started, it adds a small amount of overhead (a comparison or
// two) to every monitored function.
func FromRequest(reg *monkit.Registry, path string, query url.Values) (
	f Result, contentType string, err error) {
	first, rest := shift(path)
	second, _ := shift(rest)
	switch first {
	case "ps":
		switch second {
		case "", "text":
			return curry(reg, SpansText), "text/plain; charset=utf-8", nil
		case "dot":
			return curry(reg, SpansDot), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, SpansJSON), "application/json; charset=utf-8", nil
		}

	case "funcs":
		switch second {
		case "", "text":
			return curry(reg, FuncsText), "text/plain; charset=utf-8", nil
		case "dot":
			return curry(reg, FuncsDot), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, FuncsJSON), "application/json; charset=utf-8", nil
		}

	case "stats":
		switch second {
		case "", "text":
			return curry(reg, StatsText), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, StatsJSON), "application/json; charset=utf-8", nil
		}

	case "trace":
		regexStr := query.Get("regex")
		traceIdStr := query.Get("trace_id")
		if regexStr == "" && traceIdStr == "" {
			return nil, "", BadRequest.New("at least one of 'regex' or 'trace_id' " +
				"query parameters required")
		}
		fnMatcher := func(*monkit.Func) bool { return true }

		if regexStr != "" {
			re, err := regexp.Compile(regexStr)
			if err != nil {
				return nil, "", BadRequest.New("invalid regex %#v: %v",
					regexStr, err)
			}
			fnMatcher = func(f *monkit.Func) bool {
				return re.MatchString(f.FullName())
			}

			preselect := true
			if query.Get("preselect") != "" {
				preselect, err = strconv.ParseBool(query.Get("preselect"))
				if err != nil {
					return nil, "", BadRequest.New("invalid preselect %#v: %v",
						query.Get("preselect"), err)
				}
			}
			if preselect {
				funcs := map[*monkit.Func]bool{}
				reg.Funcs(func(f *monkit.Func) {
					if fnMatcher(f) {
						funcs[f] = true
					}
				})
				if len(funcs) <= 0 {
					return nil, "", BadRequest.New("regex preselect matches 0 functions")
				}

				fnMatcher = func(f *monkit.Func) bool { return funcs[f] }
			}
		}

		spanMatcher := func(s *monkit.Span) bool { return fnMatcher(s.Func()) }

		if traceIdStr != "" {
			traceId, err := strconv.ParseUint(traceIdStr, 16, 64)
			if err != nil {
				return nil, "", BadRequest.New(
					"trace_id expected to be hex unsigned 64 bit number: %#v", traceIdStr)
			}
			spanMatcher = func(s *monkit.Span) bool {
				return s.Trace().Id() == int64(traceId) && fnMatcher(s.Func())
			}
		}

		switch second {
		case "svg":
			return func(w io.Writer) error {
				return TraceQuerySVG(reg, w, spanMatcher)
			}, "image/svg+xml; charset=utf-8", nil
		case "json":
			return func(w io.Writer) error {
				return TraceQueryJSON(reg, w, spanMatcher)
			}, "application/json; charset=utf-8", nil
		}
	}
	return nil, "", NotFound.New("path not found: %s", path)
}

func shift(path string) (dir, left string) {
	path = strings.TrimLeft(path, "/")
	split := strings.Index(path, "/")
	if split == -1 {
		return path, ""
	}
	return path[:split], path[split:]
}
<commit_msg>buffer debug writes<commit_after>// Copyright (C) 2015 Space Monkey, 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 present

import (
	"bufio"
	"io"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"github.com/spacemonkeygo/errors"
	"github.com/spacemonkeygo/errors/errhttp"
	"gopkg.in/spacemonkeygo/monkit.v2"
)

var (
	BadRequest = errors.NewClass("Bad Request", errhttp.SetStatusCode(400))
	NotFound   = errors.NewClass("Not Found", errhttp.SetStatusCode(404))
)

// Result writes the expected data to io.Writer and returns any errors if
// found.
type Result func(io.Writer) error

func curry(reg *monkit.Registry,
	f func(*monkit.Registry, io.Writer) error) func(io.Writer) error {
	return func(w io.Writer) error {
		return f(reg, w)
	}
}

// FromRequest takes a registry (usually the Default registry), an incoming
// path, and optional query parameters, and returns a Result if possible.
//
// FromRequest understands the following paths:
//  * /ps, /ps/text       - returns the result of SpansText
//  * /ps/dot             - returns the result of SpansDot
//  * /ps/json            - returns the result of SpansJSON
//  * /funcs, /funcs/text - returns the result of FuncsText
//  * /funcs/dot          - returns the result of FuncsDot
//  * /funcs/json         - returns the result of FuncsJSON
//  * /stats, /stats/text - returns the result of StatsText
//  * /stats/json         - returns the result of StatsJSON
//  * /trace/svg          - returns the result of TraceQuerySVG
//  * /trace/json         - returns the result of TraceQueryJSON
//
// The last two paths are worth discussing in more detail, as they take
// query parameters. All trace endpoints require at least one of the following
// two query parameters:
//  * regex    - If provided, the very next Span that crosses a Func that has
//               a name that matches this regex will start a trace until that
//               triggering Span ends, provided the trace_id matches.
//  * trace_id - If provided, the very next Span on a trace with the given
//               trace id will start a trace until the triggering Span ends,
//               provided the regex matches. NOTE: the trace_id will be parsed
//               in hex.
// By default, regular expressions are matched ahead of time against all known
// Funcs, but perhaps the Func you want to trace hasn't been observed by the
// process yet, in which case the regex will fail to match anything. You can
// turn off this preselection behavior by providing preselect=false as an
// additional query param. Be advised that until a trace completes, whether
// or not it has started, it adds a small amount of overhead (a comparison or
// two) to every monitored function.
func FromRequest(reg *monkit.Registry, path string, query url.Values) (
	f Result, contentType string, err error) {

	defer func() {
		if err != nil {
			return
		}
		// wrap all functions with buffering
		unbuffered := f
		f = func(w io.Writer) (err error) {
			buf := bufio.NewWriter(w)
			err = unbuffered(buf)
			if err != nil {
				return err
			}
			err = buf.Flush()
			return err
		}
	}()

	first, rest := shift(path)
	second, _ := shift(rest)
	switch first {
	case "ps":
		switch second {
		case "", "text":
			return curry(reg, SpansText), "text/plain; charset=utf-8", nil
		case "dot":
			return curry(reg, SpansDot), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, SpansJSON), "application/json; charset=utf-8", nil
		}

	case "funcs":
		switch second {
		case "", "text":
			return curry(reg, FuncsText), "text/plain; charset=utf-8", nil
		case "dot":
			return curry(reg, FuncsDot), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, FuncsJSON), "application/json; charset=utf-8", nil
		}

	case "stats":
		switch second {
		case "", "text":
			return curry(reg, StatsText), "text/plain; charset=utf-8", nil
		case "json":
			return curry(reg, StatsJSON), "application/json; charset=utf-8", nil
		}

	case "trace":
		regexStr := query.Get("regex")
		traceIdStr := query.Get("trace_id")
		if regexStr == "" && traceIdStr == "" {
			return nil, "", BadRequest.New("at least one of 'regex' or 'trace_id' " +
				"query parameters required")
		}
		fnMatcher := func(*monkit.Func) bool { return true }

		if regexStr != "" {
			re, err := regexp.Compile(regexStr)
			if err != nil {
				return nil, "", BadRequest.New("invalid regex %#v: %v",
					regexStr, err)
			}
			fnMatcher = func(f *monkit.Func) bool {
				return re.MatchString(f.FullName())
			}

			preselect := true
			if query.Get("preselect") != "" {
				preselect, err = strconv.ParseBool(query.Get("preselect"))
				if err != nil {
					return nil, "", BadRequest.New("invalid preselect %#v: %v",
						query.Get("preselect"), err)
				}
			}
			if preselect {
				funcs := map[*monkit.Func]bool{}
				reg.Funcs(func(f *monkit.Func) {
					if fnMatcher(f) {
						funcs[f] = true
					}
				})
				if len(funcs) <= 0 {
					return nil, "", BadRequest.New("regex preselect matches 0 functions")
				}

				fnMatcher = func(f *monkit.Func) bool { return funcs[f] }
			}
		}

		spanMatcher := func(s *monkit.Span) bool { return fnMatcher(s.Func()) }

		if traceIdStr != "" {
			traceId, err := strconv.ParseUint(traceIdStr, 16, 64)
			if err != nil {
				return nil, "", BadRequest.New(
					"trace_id expected to be hex unsigned 64 bit number: %#v", traceIdStr)
			}
			spanMatcher = func(s *monkit.Span) bool {
				return s.Trace().Id() == int64(traceId) && fnMatcher(s.Func())
			}
		}

		switch second {
		case "svg":
			return func(w io.Writer) error {
				return TraceQuerySVG(reg, w, spanMatcher)
			}, "image/svg+xml; charset=utf-8", nil
		case "json":
			return func(w io.Writer) error {
				return TraceQueryJSON(reg, w, spanMatcher)
			}, "application/json; charset=utf-8", nil
		}
	}
	return nil, "", NotFound.New("path not found: %s", path)
}

func shift(path string) (dir, left string) {
	path = strings.TrimLeft(path, "/")
	split := strings.Index(path, "/")
	if split == -1 {
		return path, ""
	}
	return path[:split], path[split:]
}
<|endoftext|>


<commit_before>package migrate

import (
	"context"
	"database/sql"

	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/engine/api/group"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

func refactorGroup(ctx context.Context, db *gorp.DbMap) error {
	// First step
	// Migrate "group" entities to sign it
	tx, err := db.Begin()
	if err != nil {
		return sdk.WithStack(err)
	}

	defer tx.Rollback() // nolint

	rows, err := tx.Query(`SELECT id, name FROM "group" WHERE sig IS NULL FOR UPDATE SKIP LOCKED`)
	if err == sql.ErrNoRows {
		return nil
	}
	if err != nil {
		return sdk.WithStack(err)
	}
	defer rows.Close()

	var groups []*sdk.Group

	for rows.Next() {
		var id int64
		var name string
		if err := rows.Scan(&id, &name); err != nil {
			return sdk.WithStack(err)
		}

		g := sdk.Group{
			ID:   id,
			Name: name,
		}

		groups = append(groups, &g)
	}

	for _, g := range groups {
		if err := group.Update(ctx, tx, g); err != nil {
			return sdk.WithStack(err)
		}
	}

	if err := tx.Commit(); err != nil {
		return sdk.WithStack(err)
	}

	return nil
}

func refactorGroupMembership(ctx context.Context, db *gorp.DbMap) error {
	// Second step
	// Migrate data from table user_°group
	tx, err := db.Begin()
	if err != nil {
		return sdk.WithStack(err)
	}

	defer tx.Rollback() // nolint

	rows, err := tx.Query(`
	SELECT group_user.group_id, authentified_user_migration.authentified_user_id, group_user.group_admin
	FROM group_user
	JOIN authentified_user_migration ON authentified_user_migration.user_id = group_user.user_id
	WHERE authentified_user_migration.authentified_user_id NOT IN (
		SELECT DISTINCT authentified_user_id
		FROM group_authentified_user
	)
	FOR UPDATE SKIP LOCKED
	`)
	if err == sql.ErrNoRows {
		return nil
	}
	if err != nil {
		return sdk.WithStack(err)
	}
	defer rows.Close()

	var links []*group.LinkGroupUser

	for rows.Next() {
		var groupID int64
		var authentifiedUserID string
		var groupAdmin bool
		if err := rows.Scan(&groupID, &authentifiedUserID, &groupAdmin); err != nil {
			return sdk.WithStack(err)
		}

		var l = group.LinkGroupUser{
			GroupID:            groupID,
			AuthentifiedUserID: authentifiedUserID,
			Admin:              groupAdmin,
		}

		links = append(links, &l)
	}

	for _, l := range links {
		if err := group.InsertLinkGroupUser(ctx, tx, l); err != nil {
			return sdk.WithStack(err)
		}
	}

	if err := tx.Commit(); err != nil {
		return sdk.WithStack(err)
	}

	return nil
}

// RefactorGroupMembership .
func RefactorGroupMembership(ctx context.Context, db *gorp.DbMap) error {
	log.Debug("migrate.RefactorGroupMembership> begin")
	defer func() {
		log.Debug("migrate.RefactorGroupMembership> end")
	}()

	if err := refactorGroup(ctx, db); err != nil {
		return err
	}

	if err := refactorGroupMembership(ctx, db); err != nil {
		return err
	}

	return nil
}
<commit_msg>fix(api): migrate project_group for signature (#4973)<commit_after>package migrate

import (
	"context"
	"database/sql"

	"github.com/go-gorp/gorp"

	"github.com/ovh/cds/engine/api/group"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

func refactorGroup(ctx context.Context, db *gorp.DbMap) error {
	// First step
	// Migrate "group" entities to sign it
	tx, err := db.Begin()
	if err != nil {
		return sdk.WithStack(err)
	}

	defer tx.Rollback() // nolint

	rows, err := tx.Query(`SELECT id, name FROM "group" WHERE sig IS NULL FOR UPDATE SKIP LOCKED`)
	if err == sql.ErrNoRows {
		return nil
	}
	if err != nil {
		return sdk.WithStack(err)
	}
	defer rows.Close()

	var groups []*sdk.Group

	for rows.Next() {
		var id int64
		var name string
		if err := rows.Scan(&id, &name); err != nil {
			return sdk.WithStack(err)
		}

		g := sdk.Group{
			ID:   id,
			Name: name,
		}

		groups = append(groups, &g)
	}

	for _, g := range groups {
		if err := group.Update(ctx, tx, g); err != nil {
			return sdk.WithStack(err)
		}
	}

	if err := tx.Commit(); err != nil {
		return sdk.WithStack(err)
	}

	return nil
}

func refactorGroupMembership(ctx context.Context, db *gorp.DbMap) error {
	// Second step
	// Migrate data from table user_°group
	tx, err := db.Begin()
	if err != nil {
		return sdk.WithStack(err)
	}

	defer tx.Rollback() // nolint

	rows, err := tx.Query(`
	SELECT group_user.group_id, authentified_user_migration.authentified_user_id, group_user.group_admin
	FROM group_user
	JOIN authentified_user_migration ON authentified_user_migration.user_id = group_user.user_id
	WHERE authentified_user_migration.authentified_user_id NOT IN (
		SELECT DISTINCT authentified_user_id
		FROM group_authentified_user
	)
	FOR UPDATE SKIP LOCKED
	`)
	if err == sql.ErrNoRows {
		return nil
	}
	if err != nil {
		return sdk.WithStack(err)
	}
	defer rows.Close()

	var links []*group.LinkGroupUser

	for rows.Next() {
		var groupID int64
		var authentifiedUserID string
		var groupAdmin bool
		if err := rows.Scan(&groupID, &authentifiedUserID, &groupAdmin); err != nil {
			return sdk.WithStack(err)
		}

		var l = group.LinkGroupUser{
			GroupID:            groupID,
			AuthentifiedUserID: authentifiedUserID,
			Admin:              groupAdmin,
		}

		links = append(links, &l)
	}

	for _, l := range links {
		if err := group.InsertLinkGroupUser(ctx, tx, l); err != nil {
			return sdk.WithStack(err)
		}
	}

	if err := tx.Commit(); err != nil {
		return sdk.WithStack(err)
	}

	return nil
}

func refactorLinkGroupProject(ctx context.Context, db *gorp.DbMap) error {
	// Third step
	// Migrate data from table user_group
	tx, err := db.Begin()
	if err != nil {
		return sdk.WithStack(err)
	}

	defer tx.Rollback() // nolint

	rows, err := tx.Query(`
	SELECT id, group_id, project_id, role
	FROM project_group
	FOR UPDATE SKIP LOCKED
	`)
	if err == sql.ErrNoRows {
		return nil
	}
	if err != nil {
		return sdk.WithStack(err)
	}
	defer rows.Close()

	var links []*group.LinkGroupProject

	for rows.Next() {
		var id int64
		var groupID int64
		var projectID int64
		var role int
		if err := rows.Scan(&id, &groupID, &projectID, &role); err != nil {
			return sdk.WithStack(err)
		}

		var l = group.LinkGroupProject{
			ID:        id,
			GroupID:   groupID,
			ProjectID: projectID,
			Role:      role,
		}

		links = append(links, &l)
	}

	for _, l := range links {
		if err := group.UpdateLinkGroupProject(tx, l); err != nil {
			return sdk.WithStack(err)
		}
	}

	if err := tx.Commit(); err != nil {
		return sdk.WithStack(err)
	}

	return nil
}

// RefactorGroupMembership .
func RefactorGroupMembership(ctx context.Context, db *gorp.DbMap) error {
	log.Debug("migrate.RefactorGroupMembership> begin")
	defer func() {
		log.Debug("migrate.RefactorGroupMembership> end")
	}()

	if err := refactorGroup(ctx, db); err != nil {
		return err
	}

	if err := refactorGroupMembership(ctx, db); err != nil {
		return err
	}

	if err := refactorLinkGroupProject(ctx, db); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2019 The LUCI 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.

// Command bbagent is Buildbucket's agent running in swarming.
//
// This executable creates a luciexe 'host' environment, and runs the
// Buildbucket build's exe within this environment. Please see
// https://go.chromium.org/luci/luciexe for details about the 'luciexe'
// protocol.
//
// This command is an implementation detail of Buildbucket.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"path/filepath"
	"runtime"

	"net/http"
	_ "net/http/pprof"

	"github.com/golang/protobuf/jsonpb"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/logging/gologger"
	"go.chromium.org/luci/common/system/environ"
	"go.chromium.org/luci/lucictx"
	"go.chromium.org/luci/luciexe"
	"go.chromium.org/luci/luciexe/host"
	"go.chromium.org/luci/luciexe/invoke"

	"go.chromium.org/luci/buildbucket/cmd/bbagent/bbinput"
	bbpb "go.chromium.org/luci/buildbucket/proto"
)

func main() {
	go func() {
		// serves "/debug" endpoints for pprof.
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()
	os.Exit(mainImpl())
}

func mainImpl() int {
	ctx := logging.SetLevel(gologger.StdConfig.Use(context.Background()), logging.Info)

	check := func(err error) {
		if err != nil {
			logging.Errorf(ctx, err.Error())
			os.Exit(1)
		}
	}

	outputFile := luciexe.AddOutputFlagToSet(flag.CommandLine)
	flag.Parse()
	args := flag.Args()

	if len(args) != 1 {
		check(errors.Reason("expected 1 argument: got %d", len(args)).Err())
	}

	input, err := bbinput.Parse(args[0])
	check(errors.Annotate(err, "could not unmarshal BBAgentArgs").Err())

	sctx, err := lucictx.SwitchLocalAccount(ctx, "system")
	check(errors.Annotate(err, "could not switch to 'system' account in LUCI_CONTEXT").Err())

	bbClient, err := newBuildsClient(sctx, input.Build.Infra.Buildbucket)
	check(errors.Annotate(err, "could not connect to Buildbucket").Err())
	defer bbClient.CloseAndDrain(ctx)

	// from this point forward we want to try to report errors to buildbucket,
	// too.
	check = func(err error) {
		if err != nil {
			logging.Errorf(ctx, err.Error())
			bbClient.C <- &bbpb.Build{
				Status:          bbpb.Status_INFRA_FAILURE,
				SummaryMarkdown: fmt.Sprintf("fatal error in startup: %s", err),
			}
			bbClient.CloseAndDrain(ctx)
			os.Exit(1)
		}
	}

	cctx, cancel := context.WithCancel(ctx)
	defer cancel()

	if input.Build.GetInfra().GetResultdb().GetInvocation() != "" {
		cctx, err = setResultDBContext(cctx, input.Build)
		check(err)
	}

	opts := &host.Options{
		BaseBuild:      input.Build,
		ButlerLogLevel: logging.Warning,
		ViewerURL: fmt.Sprintf("https://%s/build/%d",
			input.Build.Infra.Buildbucket.Hostname, input.Build.Id),
	}
	opts.LogdogOutput, err = mkLogdogOutput(sctx, input.Build.Infra.Logdog)
	check(err)
	cwd, err := os.Getwd()
	check(errors.Annotate(err, "getting cwd").Err())

	opts.BaseDir = filepath.Join(cwd, "x")

	exeArgs := append(([]string)(nil), input.Build.Exe.Cmd...)
	payloadPath := input.PayloadPath
	if len(exeArgs) == 0 {
		// TODO(iannucci): delete me with ExecutablePath.
		var exe string
		payloadPath, exe = path.Split(input.ExecutablePath)
		exeArgs = []string{exe}
	}
	exePath, err := filepath.Abs(filepath.Join(payloadPath, exeArgs[0]))
	check(errors.Annotate(err, "absoluting exe path %q", input.ExecutablePath).Err())
	if runtime.GOOS == "windows" {
		exePath, err = resolveExe(exePath)
		check(errors.Annotate(err, "resolving %q", input.ExecutablePath).Err())
	}
	exeArgs[0] = exePath

	// TODO(iannucci): this is sketchy, but we preemptively add the log entries
	// for the top level user stdout/stderr streams.
	//
	// Really, `invoke.Start` is the one that knows how to arrange the
	// Output.Logs, but host.Run makes a copy of this build immediately. Find
	// a way to set these up nicely (maybe have opts.BaseBuild be a function
	// returning an immutable bbpb.Build?).
	input.Build.Output = &bbpb.Build_Output{
		Logs: []*bbpb.Log{
			{Name: "stdout", Url: "stdout"},
			{Name: "stderr", Url: "stderr"},
		},
	}
	populateSwarmingInfoFromEnv(input.Build, environ.System())

	initialJSONPB, err := (&jsonpb.Marshaler{
		OrigName: true, Indent: "  ",
	}).MarshalToString(input)
	check(errors.Annotate(err, "marshalling input args").Err())
	logging.Infof(ctx, "Input args:\n%s", initialJSONPB)

	builds, err := host.Run(cctx, opts, func(ctx context.Context, hostOpts host.Options) error {
		logging.Infof(ctx, "running luciexe: %q", exeArgs)
		logging.Infof(ctx, "  (cache dir): %q", input.CacheDir)
		subp, err := invoke.Start(ctx, exeArgs, input.Build, &invoke.Options{
			BaseDir:  hostOpts.BaseDir,
			CacheDir: input.CacheDir,
		})
		if err != nil {
			return err
		}
		_, err = subp.Wait()
		return err
	})
	if err != nil {
		check(errors.Annotate(err, "could not start luciexe host environment").Err())
	}

	var finalBuild *bbpb.Build

	// Now all we do is shuttle builds through to the buildbucket client channel
	// until there are no more builds to shuttle.
	for build := range builds {
		// TODO(iannucci): add backchannel from buildbucket prpc client to shut
		// down/cancel the build.
		bbClient.C <- build
		finalBuild = build
	}

	check(errors.Annotate(
		outputFile.Write(finalBuild), "writing final build").Err())

	if finalBuild.Status != bbpb.Status_SUCCESS {
		return 1
	}
	return 0
}

func resolveExe(path string) (string, error) {
	if filepath.Ext(path) != "" {
		return path, nil
	}

	lme := errors.NewLazyMultiError(2)
	for i, ext := range []string{".exe", ".bat"} {
		candidate := path + ext
		if _, err := os.Stat(candidate); !lme.Assign(i, err) {
			return candidate, nil
		}
	}

	me := lme.Get().(errors.MultiError)
	return path, errors.Reason("cannot find .exe (%q) or .bat (%q)", me[0], me[1]).Err()
}
<commit_msg>[bbagent] Properly fill KnownGerritHosts.<commit_after>// Copyright 2019 The LUCI 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.

// Command bbagent is Buildbucket's agent running in swarming.
//
// This executable creates a luciexe 'host' environment, and runs the
// Buildbucket build's exe within this environment. Please see
// https://go.chromium.org/luci/luciexe for details about the 'luciexe'
// protocol.
//
// This command is an implementation detail of Buildbucket.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"path/filepath"
	"runtime"

	"net/http"
	_ "net/http/pprof"

	"github.com/golang/protobuf/jsonpb"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/logging/gologger"
	"go.chromium.org/luci/common/system/environ"
	"go.chromium.org/luci/lucictx"
	"go.chromium.org/luci/luciexe"
	"go.chromium.org/luci/luciexe/host"
	"go.chromium.org/luci/luciexe/invoke"

	"go.chromium.org/luci/buildbucket/cmd/bbagent/bbinput"
	bbpb "go.chromium.org/luci/buildbucket/proto"
)

func main() {
	go func() {
		// serves "/debug" endpoints for pprof.
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()
	os.Exit(mainImpl())
}

func mainImpl() int {
	ctx := logging.SetLevel(gologger.StdConfig.Use(context.Background()), logging.Info)

	check := func(err error) {
		if err != nil {
			logging.Errorf(ctx, err.Error())
			os.Exit(1)
		}
	}

	outputFile := luciexe.AddOutputFlagToSet(flag.CommandLine)
	flag.Parse()
	args := flag.Args()

	if len(args) != 1 {
		check(errors.Reason("expected 1 argument: got %d", len(args)).Err())
	}

	input, err := bbinput.Parse(args[0])
	check(errors.Annotate(err, "could not unmarshal BBAgentArgs").Err())

	sctx, err := lucictx.SwitchLocalAccount(ctx, "system")
	check(errors.Annotate(err, "could not switch to 'system' account in LUCI_CONTEXT").Err())

	bbClient, err := newBuildsClient(sctx, input.Build.Infra.Buildbucket)
	check(errors.Annotate(err, "could not connect to Buildbucket").Err())
	defer bbClient.CloseAndDrain(ctx)

	// from this point forward we want to try to report errors to buildbucket,
	// too.
	check = func(err error) {
		if err != nil {
			logging.Errorf(ctx, err.Error())
			bbClient.C <- &bbpb.Build{
				Status:          bbpb.Status_INFRA_FAILURE,
				SummaryMarkdown: fmt.Sprintf("fatal error in startup: %s", err),
			}
			bbClient.CloseAndDrain(ctx)
			os.Exit(1)
		}
	}

	cctx, cancel := context.WithCancel(ctx)
	defer cancel()

	if input.Build.GetInfra().GetResultdb().GetInvocation() != "" {
		cctx, err = setResultDBContext(cctx, input.Build)
		check(err)
	}

	opts := &host.Options{
		BaseBuild:      input.Build,
		ButlerLogLevel: logging.Warning,
		ViewerURL: fmt.Sprintf("https://%s/build/%d",
			input.Build.Infra.Buildbucket.Hostname, input.Build.Id),
		ExeAuth: host.DefaultExeAuth("bbagent", input.KnownPublicGerritHosts),
	}
	opts.LogdogOutput, err = mkLogdogOutput(sctx, input.Build.Infra.Logdog)
	check(err)
	cwd, err := os.Getwd()
	check(errors.Annotate(err, "getting cwd").Err())

	opts.BaseDir = filepath.Join(cwd, "x")

	exeArgs := append(([]string)(nil), input.Build.Exe.Cmd...)
	payloadPath := input.PayloadPath
	if len(exeArgs) == 0 {
		// TODO(iannucci): delete me with ExecutablePath.
		var exe string
		payloadPath, exe = path.Split(input.ExecutablePath)
		exeArgs = []string{exe}
	}
	exePath, err := filepath.Abs(filepath.Join(payloadPath, exeArgs[0]))
	check(errors.Annotate(err, "absoluting exe path %q", input.ExecutablePath).Err())
	if runtime.GOOS == "windows" {
		exePath, err = resolveExe(exePath)
		check(errors.Annotate(err, "resolving %q", input.ExecutablePath).Err())
	}
	exeArgs[0] = exePath

	// TODO(iannucci): this is sketchy, but we preemptively add the log entries
	// for the top level user stdout/stderr streams.
	//
	// Really, `invoke.Start` is the one that knows how to arrange the
	// Output.Logs, but host.Run makes a copy of this build immediately. Find
	// a way to set these up nicely (maybe have opts.BaseBuild be a function
	// returning an immutable bbpb.Build?).
	input.Build.Output = &bbpb.Build_Output{
		Logs: []*bbpb.Log{
			{Name: "stdout", Url: "stdout"},
			{Name: "stderr", Url: "stderr"},
		},
	}
	populateSwarmingInfoFromEnv(input.Build, environ.System())

	initialJSONPB, err := (&jsonpb.Marshaler{
		OrigName: true, Indent: "  ",
	}).MarshalToString(input)
	check(errors.Annotate(err, "marshalling input args").Err())
	logging.Infof(ctx, "Input args:\n%s", initialJSONPB)

	builds, err := host.Run(cctx, opts, func(ctx context.Context, hostOpts host.Options) error {
		logging.Infof(ctx, "running luciexe: %q", exeArgs)
		logging.Infof(ctx, "  (cache dir): %q", input.CacheDir)
		subp, err := invoke.Start(ctx, exeArgs, input.Build, &invoke.Options{
			BaseDir:  hostOpts.BaseDir,
			CacheDir: input.CacheDir,
		})
		if err != nil {
			return err
		}
		_, err = subp.Wait()
		return err
	})
	if err != nil {
		check(errors.Annotate(err, "could not start luciexe host environment").Err())
	}

	var finalBuild *bbpb.Build

	// Now all we do is shuttle builds through to the buildbucket client channel
	// until there are no more builds to shuttle.
	for build := range builds {
		// TODO(iannucci): add backchannel from buildbucket prpc client to shut
		// down/cancel the build.
		bbClient.C <- build
		finalBuild = build
	}

	check(errors.Annotate(
		outputFile.Write(finalBuild), "writing final build").Err())

	if finalBuild.Status != bbpb.Status_SUCCESS {
		return 1
	}
	return 0
}

func resolveExe(path string) (string, error) {
	if filepath.Ext(path) != "" {
		return path, nil
	}

	lme := errors.NewLazyMultiError(2)
	for i, ext := range []string{".exe", ".bat"} {
		candidate := path + ext
		if _, err := os.Stat(candidate); !lme.Assign(i, err) {
			return candidate, nil
		}
	}

	me := lme.Get().(errors.MultiError)
	return path, errors.Reason("cannot find .exe (%q) or .bat (%q)", me[0], me[1]).Err()
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes 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.
*/

// buildifier defines a Prow plugin that runs buildifier over modified BUILD,
// WORKSPACE, and skylark (.bzl) files in pull requests.
package buildifier

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"

	"github.com/bazelbuild/buildtools/build"
	"github.com/sirupsen/logrus"

	"k8s.io/test-infra/prow/genfiles"
	"k8s.io/test-infra/prow/git"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/plugins"
)

const (
	pluginName  = "buildifier"
	maxComments = 20
)

var buildifyRe = regexp.MustCompile(`(?mi)^/buildif(y|ier)\s*$`)

func init() {
	plugins.RegisterGenericCommentHandler(pluginName, handleGenericComment)
}

type githubClient interface {
	GetFile(org, repo, filepath, commit string) ([]byte, error)
	GetPullRequest(org, repo string, number int) (*github.PullRequest, error)
	GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error)
	CreateReview(org, repo string, number int, r github.DraftReview) error
	ListPullRequestComments(org, repo string, number int) ([]github.ReviewComment, error)
}

func handleGenericComment(pc plugins.PluginClient, e github.GenericCommentEvent) error {
	return handle(pc.GitHubClient, pc.GitClient, pc.Logger, &e)
}

// modifiedBazelFiles returns a map from filename to patch string for all Bazel files
// that are modified in the PR.
func modifiedBazelFiles(ghc githubClient, org, repo string, number int, sha string) (map[string]string, error) {
	changes, err := ghc.GetPullRequestChanges(org, repo, number)
	if err != nil {
		return nil, err
	}

	gfg, err := genfiles.NewGroup(ghc, org, repo, sha)
	if err != nil {
		return nil, err
	}

	modifiedFiles := make(map[string]string)
	for _, change := range changes {
		switch {
		case gfg.Match(change.Filename):
			continue
		case change.Status == github.PullRequestFileRemoved || change.Status == github.PullRequestFileRenamed:
			continue
		case strings.Contains(change.Filename, "BUILD"):
			break
		case strings.Contains(change.Filename, "WORKSPACE"):
			break
		case filepath.Ext(change.Filename) != ".bzl":
			continue
		}
		modifiedFiles[change.Filename] = change.Patch
	}
	return modifiedFiles, nil
}

func uniqProblems(problems []string) []string {
	sort.Strings(problems)
	var uniq []string
	last := ""
	for _, s := range problems {
		if s != last {
			last = s
			uniq = append(uniq, s)
		}
	}
	return uniq
}

// problemsInFiles runs buildifier on the files. It returns a map from the file to
// a list of problems with that file.
func problemsInFiles(r *git.Repo, files map[string]string) (map[string][]string, error) {
	problems := make(map[string][]string)
	for f := range files {
		src, err := ioutil.ReadFile(filepath.Join(r.Dir, f))
		if err != nil {
			return nil, err
		}
		// This is modeled after the logic from buildifier:
		// https://github.com/bazelbuild/buildtools/blob/master/buildifier/buildifier.go#L261
		content, err := build.Parse(f, src)
		if err != nil {
			return nil, fmt.Errorf("parsing as Bazel file %v", err)
		}
		beforeRewrite := build.Format(content)
		var info build.RewriteInfo
		build.Rewrite(content, &info)
		ndata := build.Format(content)
		if !bytes.Equal(src, ndata) {
			if !bytes.Equal(src, beforeRewrite) {
				// TODO(mattmoor): This always seems to be empty?
				problems[f] = uniqProblems(info.Log)
			}
		}
	}
	return problems, nil
}

func handle(ghc githubClient, gc *git.Client, log *logrus.Entry, e *github.GenericCommentEvent) error {
	// Only handle open PRs and new requests.
	if e.IssueState != "open" || !e.IsPR || e.Action != github.GenericCommentActionCreated {
		return nil
	}
	if !buildifyRe.MatchString(e.Body) {
		return nil
	}

	org := e.Repo.Owner.Login
	repo := e.Repo.Name

	pr, err := ghc.GetPullRequest(org, repo, e.Number)
	if err != nil {
		return err
	}

	// List modified files.
	modifiedFiles, err := modifiedBazelFiles(ghc, org, repo, pr.Number, pr.Head.SHA)
	if err != nil {
		return err
	}
	if len(modifiedFiles) == 0 {
		return nil
	}
	log.Infof("Will buildify %d modified Bazel files.", len(modifiedFiles))

	// Clone the repo, checkout the PR.
	startClone := time.Now()
	r, err := gc.Clone(e.Repo.FullName)
	if err != nil {
		return err
	}
	defer func() {
		if err := r.Clean(); err != nil {
			log.WithError(err).Error("Error cleaning up repo.")
		}
	}()
	if err := r.CheckoutPullRequest(e.Number); err != nil {
		return err
	}
	finishClone := time.Now()
	log.WithField("duration", time.Since(startClone)).Info("Cloned and checked out PR.")

	// Compute buildifier errors.
	problems, err := problemsInFiles(r, modifiedFiles)
	if err != nil {
		return err
	}
	log.WithField("duration", time.Since(finishClone)).Info("Buildified.")

	// Make the list of comments.
	var comments []github.DraftReviewComment
	for f := range problems {
		comments = append(comments, github.DraftReviewComment{
			Path: f,
			// TODO(mattmoor): Include the messages if they are ever non-empty.
			Body: strings.Join([]string{
				"This Bazel file needs formatting, run:",
				"```shell",
				fmt.Sprintf("buildifier -mode=fix %q", f),
				"```"}, "\n"),
			Position: 1,
		})
	}

	// Trim down the number of comments if necessary.
	totalProblems := len(problems)

	// Make the review body.
	s := "s"
	if totalProblems == 1 {
		s = ""
	}
	response := fmt.Sprintf("%d warning%s.", totalProblems, s)

	return ghc.CreateReview(org, repo, e.Number, github.DraftReview{
		Body:     plugins.FormatResponseRaw(e.Body, e.HTMLURL, e.User.Login, response),
		Action:   github.Comment,
		Comments: comments,
	})
}
<commit_msg>Change buildifier link to use commit-ish.<commit_after>/*
Copyright 2017 The Kubernetes 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.
*/

// buildifier defines a Prow plugin that runs buildifier over modified BUILD,
// WORKSPACE, and skylark (.bzl) files in pull requests.
package buildifier

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"

	"github.com/bazelbuild/buildtools/build"
	"github.com/sirupsen/logrus"

	"k8s.io/test-infra/prow/genfiles"
	"k8s.io/test-infra/prow/git"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/plugins"
)

const (
	pluginName  = "buildifier"
	maxComments = 20
)

var buildifyRe = regexp.MustCompile(`(?mi)^/buildif(y|ier)\s*$`)

func init() {
	plugins.RegisterGenericCommentHandler(pluginName, handleGenericComment)
}

type githubClient interface {
	GetFile(org, repo, filepath, commit string) ([]byte, error)
	GetPullRequest(org, repo string, number int) (*github.PullRequest, error)
	GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error)
	CreateReview(org, repo string, number int, r github.DraftReview) error
	ListPullRequestComments(org, repo string, number int) ([]github.ReviewComment, error)
}

func handleGenericComment(pc plugins.PluginClient, e github.GenericCommentEvent) error {
	return handle(pc.GitHubClient, pc.GitClient, pc.Logger, &e)
}

// modifiedBazelFiles returns a map from filename to patch string for all Bazel files
// that are modified in the PR.
func modifiedBazelFiles(ghc githubClient, org, repo string, number int, sha string) (map[string]string, error) {
	changes, err := ghc.GetPullRequestChanges(org, repo, number)
	if err != nil {
		return nil, err
	}

	gfg, err := genfiles.NewGroup(ghc, org, repo, sha)
	if err != nil {
		return nil, err
	}

	modifiedFiles := make(map[string]string)
	for _, change := range changes {
		switch {
		case gfg.Match(change.Filename):
			continue
		case change.Status == github.PullRequestFileRemoved || change.Status == github.PullRequestFileRenamed:
			continue
		case strings.Contains(change.Filename, "BUILD"):
			break
		case strings.Contains(change.Filename, "WORKSPACE"):
			break
		case filepath.Ext(change.Filename) != ".bzl":
			continue
		}
		modifiedFiles[change.Filename] = change.Patch
	}
	return modifiedFiles, nil
}

func uniqProblems(problems []string) []string {
	sort.Strings(problems)
	var uniq []string
	last := ""
	for _, s := range problems {
		if s != last {
			last = s
			uniq = append(uniq, s)
		}
	}
	return uniq
}

// problemsInFiles runs buildifier on the files. It returns a map from the file to
// a list of problems with that file.
func problemsInFiles(r *git.Repo, files map[string]string) (map[string][]string, error) {
	problems := make(map[string][]string)
	for f := range files {
		src, err := ioutil.ReadFile(filepath.Join(r.Dir, f))
		if err != nil {
			return nil, err
		}
		// This is modeled after the logic from buildifier:
		// https://github.com/bazelbuild/buildtools/blob/8818289/buildifier/buildifier.go#L261
		content, err := build.Parse(f, src)
		if err != nil {
			return nil, fmt.Errorf("parsing as Bazel file %v", err)
		}
		beforeRewrite := build.Format(content)
		var info build.RewriteInfo
		build.Rewrite(content, &info)
		ndata := build.Format(content)
		if !bytes.Equal(src, ndata) && !bytes.Equal(src, beforeRewrite) {
			// TODO(mattmoor): This always seems to be empty?
			problems[f] = uniqProblems(info.Log)
		}
	}
	return problems, nil
}

func handle(ghc githubClient, gc *git.Client, log *logrus.Entry, e *github.GenericCommentEvent) error {
	// Only handle open PRs and new requests.
	if e.IssueState != "open" || !e.IsPR || e.Action != github.GenericCommentActionCreated {
		return nil
	}
	if !buildifyRe.MatchString(e.Body) {
		return nil
	}

	org := e.Repo.Owner.Login
	repo := e.Repo.Name

	pr, err := ghc.GetPullRequest(org, repo, e.Number)
	if err != nil {
		return err
	}

	// List modified files.
	modifiedFiles, err := modifiedBazelFiles(ghc, org, repo, pr.Number, pr.Head.SHA)
	if err != nil {
		return err
	}
	if len(modifiedFiles) == 0 {
		return nil
	}
	log.Infof("Will buildify %d modified Bazel files.", len(modifiedFiles))

	// Clone the repo, checkout the PR.
	startClone := time.Now()
	r, err := gc.Clone(e.Repo.FullName)
	if err != nil {
		return err
	}
	defer func() {
		if err := r.Clean(); err != nil {
			log.WithError(err).Error("Error cleaning up repo.")
		}
	}()
	if err := r.CheckoutPullRequest(e.Number); err != nil {
		return err
	}
	finishClone := time.Now()
	log.WithField("duration", time.Since(startClone)).Info("Cloned and checked out PR.")

	// Compute buildifier errors.
	problems, err := problemsInFiles(r, modifiedFiles)
	if err != nil {
		return err
	}
	log.WithField("duration", time.Since(finishClone)).Info("Buildified.")

	// Make the list of comments.
	var comments []github.DraftReviewComment
	for f := range problems {
		comments = append(comments, github.DraftReviewComment{
			Path: f,
			// TODO(mattmoor): Include the messages if they are ever non-empty.
			Body: strings.Join([]string{
				"This Bazel file needs formatting, run:",
				"```shell",
				fmt.Sprintf("buildifier -mode=fix %q", f),
				"```"}, "\n"),
			Position: 1,
		})
	}

	// Trim down the number of comments if necessary.
	totalProblems := len(problems)

	// Make the review body.
	s := "s"
	if totalProblems == 1 {
		s = ""
	}
	response := fmt.Sprintf("%d warning%s.", totalProblems, s)

	return ghc.CreateReview(org, repo, e.Number, github.DraftReview{
		Body:     plugins.FormatResponseRaw(e.Body, e.HTMLURL, e.User.Login, response),
		Action:   github.Comment,
		Comments: comments,
	})
}
<|endoftext|>


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

/*
Generating random text: a Markov chain algorithm

Based on the program presented in the "Design and Implementation" chapter
of The Practice of Programming (Kernighan and Pike, Addison-Wesley 1999).
See also Computer Recreations, Scientific American 260, 122 - 125 (1989).

A Markov chain algorithm generates text by creating a statistical model of
potential textual suffixes for a given prefix. Consider this text:

	I am not a number! I am a free man!

Our Markov chain algorithm would arrange this text into this set of prefixes
and suffixes, or "chain": (This table assumes a prefix length of two words.)

	Prefix       Suffix

	"" ""        I
	"" I         am
	I am         a
	I am         not
	a free       man!
	am a         free
	am not       a
	a number!    I
	number! I    am
	not a        number!

To generate text using this table we select an initial prefix ("I am", for
example), choose one of the suffixes associated with that prefix at random
with probability determined by the input statistics ("a"),
and then create a new prefix by removing the first word from the prefix
and appending the suffix (making the new prefix is "am a"). Repeat this process
until we can't find any suffixes for the current prefix or we exceed the word
limit. (The word limit is necessary as the chain table may contain cycles.)

Our version of this program reads text from standard input, parsing it into a
Markov chain, and writes generated text to standard output.
The prefix and output lengths can be specified using the -prefix and -words
flags on the command-line.
*/
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"math/rand"
	"os"
	"strings"
	"time"
)

// Prefix is a Markov chain prefix of one or more words.
type Prefix []string

// String returns the Prefix as a string (for use as a map key).
func (p Prefix) String() string {
	return strings.Join(p, " ")
}

// Shift removes the first word from the Prefix and appends the given word.
func (p Prefix) Shift(word string) {
	copy(p, p[1:])
	p[len(p)-1] = word
}

// Chain contains a map ("chain") of prefixes to a list of suffixes.
// A prefix is a string of prefixLen words joined with spaces.
// A suffix is a single word. A prefix can have multiple suffixes.
type Chain struct {
	chain     map[string][]string
	prefixLen int
}

// NewChain returns a new Chain with prefixes of prefixLen words.
func NewChain(prefixLen int) *Chain {
	return &Chain{make(map[string][]string), prefixLen}
}

// Build reads text from the provided Reader and
// parses it into prefixes and suffixes that are stored in Chain.
func (c *Chain) Build(r io.Reader) {
	br := bufio.NewReader(r)
	p := make(Prefix, c.prefixLen)
	for {
		var s string
		if _, err := fmt.Fscan(br, &s); err != nil {
			break
		}
		key := p.String()
		c.chain[key] = append(c.chain[key], s)
		p.Shift(s)
	}
}

// Generate returns a string of at most n words generated from Chain.
func (c *Chain) Generate(n int) string {
	p := make(Prefix, c.prefixLen)
	var words []string
	for i := 0; i < n; i++ {
		choices := c.chain[p.String()]
		if len(choices) == 0 {
			break
		}
		next := choices[rand.Intn(len(choices))]
		words = append(words, next)
		p.Shift(next)
	}
	return strings.Join(words, " ")
}

func main() {
	// Register command-line flags.
	numWords := flag.Int("words", 100, "maximum number of words to print")
	prefixLen := flag.Int("prefix", 2, "prefix length in words")

	flag.Parse()                  // Parse command-line flags.
	rand.Seed(time.Nanoseconds()) // Seed the random number generator.

	c := NewChain(*prefixLen)     // Initialize a new Chain.
	c.Build(os.Stdin)             // Build chains from standard input.
	text := c.Generate(*numWords) // Generate text.
	fmt.Println(text)             // Write text to standard output.
}
<commit_msg>doc: fix codewalk/markov.go<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Generating random text: a Markov chain algorithm

Based on the program presented in the "Design and Implementation" chapter
of The Practice of Programming (Kernighan and Pike, Addison-Wesley 1999).
See also Computer Recreations, Scientific American 260, 122 - 125 (1989).

A Markov chain algorithm generates text by creating a statistical model of
potential textual suffixes for a given prefix. Consider this text:

	I am not a number! I am a free man!

Our Markov chain algorithm would arrange this text into this set of prefixes
and suffixes, or "chain": (This table assumes a prefix length of two words.)

	Prefix       Suffix

	"" ""        I
	"" I         am
	I am         a
	I am         not
	a free       man!
	am a         free
	am not       a
	a number!    I
	number! I    am
	not a        number!

To generate text using this table we select an initial prefix ("I am", for
example), choose one of the suffixes associated with that prefix at random
with probability determined by the input statistics ("a"),
and then create a new prefix by removing the first word from the prefix
and appending the suffix (making the new prefix is "am a"). Repeat this process
until we can't find any suffixes for the current prefix or we exceed the word
limit. (The word limit is necessary as the chain table may contain cycles.)

Our version of this program reads text from standard input, parsing it into a
Markov chain, and writes generated text to standard output.
The prefix and output lengths can be specified using the -prefix and -words
flags on the command-line.
*/
package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"math/rand"
	"os"
	"strings"
	"time"
)

// Prefix is a Markov chain prefix of one or more words.
type Prefix []string

// String returns the Prefix as a string (for use as a map key).
func (p Prefix) String() string {
	return strings.Join(p, " ")
}

// Shift removes the first word from the Prefix and appends the given word.
func (p Prefix) Shift(word string) {
	copy(p, p[1:])
	p[len(p)-1] = word
}

// Chain contains a map ("chain") of prefixes to a list of suffixes.
// A prefix is a string of prefixLen words joined with spaces.
// A suffix is a single word. A prefix can have multiple suffixes.
type Chain struct {
	chain     map[string][]string
	prefixLen int
}

// NewChain returns a new Chain with prefixes of prefixLen words.
func NewChain(prefixLen int) *Chain {
	return &Chain{make(map[string][]string), prefixLen}
}

// Build reads text from the provided Reader and
// parses it into prefixes and suffixes that are stored in Chain.
func (c *Chain) Build(r io.Reader) {
	br := bufio.NewReader(r)
	p := make(Prefix, c.prefixLen)
	for {
		var s string
		if _, err := fmt.Fscan(br, &s); err != nil {
			break
		}
		key := p.String()
		c.chain[key] = append(c.chain[key], s)
		p.Shift(s)
	}
}

// Generate returns a string of at most n words generated from Chain.
func (c *Chain) Generate(n int) string {
	p := make(Prefix, c.prefixLen)
	var words []string
	for i := 0; i < n; i++ {
		choices := c.chain[p.String()]
		if len(choices) == 0 {
			break
		}
		next := choices[rand.Intn(len(choices))]
		words = append(words, next)
		p.Shift(next)
	}
	return strings.Join(words, " ")
}

func main() {
	// Register command-line flags.
	numWords := flag.Int("words", 100, "maximum number of words to print")
	prefixLen := flag.Int("prefix", 2, "prefix length in words")

	flag.Parse()                     // Parse command-line flags.
	rand.Seed(time.Now().UnixNano()) // Seed the random number generator.

	c := NewChain(*prefixLen)     // Initialize a new Chain.
	c.Build(os.Stdin)             // Build chains from standard input.
	text := c.Generate(*numWords) // Generate text.
	fmt.Println(text)             // Write text to standard output.
}
<|endoftext|>


<commit_before>package caddy

import (
	"fmt"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	. "github.com/stretchr/testify/assert"
	"github.com/tarent/loginsrv/login"
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func TestSetup(t *testing.T) {

	os.Setenv("JWT_SECRET", "jwtsecret")

	for j, test := range []struct {
		input     string
		shouldErr bool
		config    login.Config
	}{
		{ //defaults
			input: `login {
                                        simple bob=secret
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      24 * time.Hour,
				SuccessURL:     "/",
				LoginPath:      "/login",
				CookieName:     "jwt_token",
				CookieHTTPOnly: true,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},
		{
			input: `login {
                                        success_url successurl
                                        jwt_expiry 42h
                                        login_path /foo/bar
                                        cookie_name cookiename
                                        cookie_http_only false
                                        cookie_domain example.com
                                        cookie_expiry 23h23m
                                        simple bob=secret
                                        osiam endpoint=http://localhost:8080,client_id=example-client,client_secret=secret
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      42 * time.Hour,
				SuccessURL:     "successurl",
				LoginPath:      "/foo/bar",
				CookieName:     "cookiename",
				CookieDomain:   "example.com",
				CookieExpiry:   23*time.Hour + 23*time.Minute,
				CookieHTTPOnly: false,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
					"osiam": map[string]string{
						"endpoint":      "http://localhost:8080",
						"client_id":     "example-client",
						"client_secret": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},
		{ // backwards compatibility
			// * login path as argument
			// * '-' in parameter names
			// * backend config by 'backend provider='
			input: `loginsrv /context {
                                        backend provider=simple,bob=secret
                                        cookie-name cookiename
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      24 * time.Hour,
				SuccessURL:     "/",
				LoginPath:      "/context/login",
				CookieName:     "cookiename",
				CookieHTTPOnly: true,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},
		{ // backwards compatibility
			// * login path as argument
			// * '-' in parameter names
			// * backend config by 'backend provider='
			input: `loginsrv / {
                                        backend provider=simple,bob=secret
                                        cookie-name cookiename
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      24 * time.Hour,
				SuccessURL:     "/",
				LoginPath:      "/login",
				CookieName:     "cookiename",
				CookieHTTPOnly: true,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},

		// error cases
		{ // duration parse error
			input: `login {
                                        simple bob=secret
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      24 * time.Hour,
				SuccessURL:     "/",
				LoginPath:      "/login",
				CookieName:     "jwt_token",
				CookieHTTPOnly: true,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},
		{input: "login {\n}", shouldErr: true},
		{input: "login xx yy {\n}", shouldErr: true},
		{input: "login {\n cookie_http_only 42d \n simple bob=secret \n}", shouldErr: true},
		{input: "login {\n unknown property \n simple bob=secret \n}", shouldErr: true},
		{input: "login {\n backend \n}", shouldErr: true},
		{input: "login {\n backend provider=foo\n}", shouldErr: true},
		{input: "login {\n backend kk\n}", shouldErr: true},
	} {
		t.Run(fmt.Sprintf("test %v", j), func(t *testing.T) {
			c := caddy.NewTestController("http", test.input)
			err := setup(c)
			if test.shouldErr {
				Error(t, err, "test ")
				return
			}
			NoError(t, err)
			mids := httpserver.GetConfig(c).Middleware()
			if len(mids) == 0 {
				t.Errorf("no middlewares created in test #%v", j)
				return
			}
			middleware := mids[len(mids)-1](nil).(*CaddyHandler)
			Equal(t, &test.config, middleware.config)
		})
	}
}

func TestSetup_RelativeTemplateFile(t *testing.T) {
	caddyfile := "loginsrv {\n  template myTemplate.tpl\n  simple bob=secret\n}"
	root, _ := ioutil.TempDir("", "")
	expectedPath := root + "/myTemplate.tpl"

	c := caddy.NewTestController("http", caddyfile)
	c.Key = "RelativeTemplateFileTest"
	config := httpserver.GetConfig(c)
	config.Root = root

	err := setup(c)
	NoError(t, err)
	mids := httpserver.GetConfig(c).Middleware()
	if len(mids) == 0 {
		t.Errorf("no middlewares created")
	}
	middleware := mids[len(mids)-1](nil).(*CaddyHandler)

	Equal(t, expectedPath, middleware.config.Template)
}
<commit_msg>Minor correction for cross-platform filepath<commit_after>package caddy

import (
	"fmt"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
	. "github.com/stretchr/testify/assert"
	"github.com/tarent/loginsrv/login"
	"io/ioutil"
	"path/filepath"
	"os"
	"testing"
	"time"
)

func TestSetup(t *testing.T) {

	os.Setenv("JWT_SECRET", "jwtsecret")

	for j, test := range []struct {
		input     string
		shouldErr bool
		config    login.Config
	}{
		{ //defaults
			input: `login {
                                        simple bob=secret
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      24 * time.Hour,
				SuccessURL:     "/",
				LoginPath:      "/login",
				CookieName:     "jwt_token",
				CookieHTTPOnly: true,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},
		{
			input: `login {
                                        success_url successurl
                                        jwt_expiry 42h
                                        login_path /foo/bar
                                        cookie_name cookiename
                                        cookie_http_only false
                                        cookie_domain example.com
                                        cookie_expiry 23h23m
                                        simple bob=secret
                                        osiam endpoint=http://localhost:8080,client_id=example-client,client_secret=secret
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      42 * time.Hour,
				SuccessURL:     "successurl",
				LoginPath:      "/foo/bar",
				CookieName:     "cookiename",
				CookieDomain:   "example.com",
				CookieExpiry:   23*time.Hour + 23*time.Minute,
				CookieHTTPOnly: false,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
					"osiam": map[string]string{
						"endpoint":      "http://localhost:8080",
						"client_id":     "example-client",
						"client_secret": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},
		{ // backwards compatibility
			// * login path as argument
			// * '-' in parameter names
			// * backend config by 'backend provider='
			input: `loginsrv /context {
                                        backend provider=simple,bob=secret
                                        cookie-name cookiename
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      24 * time.Hour,
				SuccessURL:     "/",
				LoginPath:      "/context/login",
				CookieName:     "cookiename",
				CookieHTTPOnly: true,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},
		{ // backwards compatibility
			// * login path as argument
			// * '-' in parameter names
			// * backend config by 'backend provider='
			input: `loginsrv / {
                                        backend provider=simple,bob=secret
                                        cookie-name cookiename
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      24 * time.Hour,
				SuccessURL:     "/",
				LoginPath:      "/login",
				CookieName:     "cookiename",
				CookieHTTPOnly: true,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},

		// error cases
		{ // duration parse error
			input: `login {
                                        simple bob=secret
                                }`,
			shouldErr: false,
			config: login.Config{
				JwtSecret:      "jwtsecret",
				JwtExpiry:      24 * time.Hour,
				SuccessURL:     "/",
				LoginPath:      "/login",
				CookieName:     "jwt_token",
				CookieHTTPOnly: true,
				Backends: login.Options{
					"simple": map[string]string{
						"bob": "secret",
					},
				},
				Oauth:       login.Options{},
				GracePeriod: 5 * time.Second,
			}},
		{input: "login {\n}", shouldErr: true},
		{input: "login xx yy {\n}", shouldErr: true},
		{input: "login {\n cookie_http_only 42d \n simple bob=secret \n}", shouldErr: true},
		{input: "login {\n unknown property \n simple bob=secret \n}", shouldErr: true},
		{input: "login {\n backend \n}", shouldErr: true},
		{input: "login {\n backend provider=foo\n}", shouldErr: true},
		{input: "login {\n backend kk\n}", shouldErr: true},
	} {
		t.Run(fmt.Sprintf("test %v", j), func(t *testing.T) {
			c := caddy.NewTestController("http", test.input)
			err := setup(c)
			if test.shouldErr {
				Error(t, err, "test ")
				return
			}
			NoError(t, err)
			mids := httpserver.GetConfig(c).Middleware()
			if len(mids) == 0 {
				t.Errorf("no middlewares created in test #%v", j)
				return
			}
			middleware := mids[len(mids)-1](nil).(*CaddyHandler)
			Equal(t, &test.config, middleware.config)
		})
	}
}

func TestSetup_RelativeTemplateFile(t *testing.T) {
	caddyfile := "loginsrv {\n  template myTemplate.tpl\n  simple bob=secret\n}"
	root, _ := ioutil.TempDir("", "")
	expectedPath := filepath.FromSlash(root + "/myTemplate.tpl")

	c := caddy.NewTestController("http", caddyfile)
	c.Key = "RelativeTemplateFileTest"
	config := httpserver.GetConfig(c)
	config.Root = root

	err := setup(c)
	NoError(t, err)
	mids := httpserver.GetConfig(c).Middleware()
	if len(mids) == 0 {
		t.Errorf("no middlewares created")
	}
	middleware := mids[len(mids)-1](nil).(*CaddyHandler)

	Equal(t, expectedPath, middleware.config.Template)
}
<|endoftext|>


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

package chunkenc

import (
	"fmt"
	"math"
	"sync"

	"github.com/pkg/errors"
)

// Encoding is the identifier for a chunk encoding.
type Encoding uint8

func (e Encoding) String() string {
	switch e {
	case EncNone:
		return "none"
	case EncXOR:
		return "XOR"
	}
	return "<unknown>"
}

// The different available chunk encodings.
const (
	EncNone Encoding = iota
	EncXOR
)

// Chunk holds a sequence of sample pairs that can be iterated over and appended to.
type Chunk interface {
	// Bytes returns the underlying byte slice of the chunk.
	Bytes() []byte

	// Encoding returns the encoding type of the chunk.
	Encoding() Encoding

	// Appender returns an appender to append samples to the chunk.
	Appender() (Appender, error)

	// The iterator passed as argument is for re-use.
	// Depending on implementation, the iterator can
	// be re-used or a new iterator can be allocated.
	Iterator(Iterator) Iterator

	// NumSamples returns the number of samples in the chunk.
	NumSamples() int

	// Compact is called whenever a chunk is expected to be complete (no more
	// samples appended) and the underlying implementation can eventually
	// optimize the chunk.
	// There's no strong guarantee that no samples will be appended once
	// Compact() is called. Implementing this function is optional.
	Compact()
}

// Appender adds sample pairs to a chunk.
type Appender interface {
	Append(int64, float64)
}

// Iterator is a simple iterator that can only get the next value.
// Iterator iterates over the samples of a time series.
type Iterator interface {
	// Next advances the iterator by one.
	Next() bool
	// Seek advances the iterator forward to the first sample with the timestamp equal or greater than t.
	// If current sample found by previous `Next` or `Seek` operation already has this property, Seek has no effect.
	// Seek returns true, if such sample exists, false otherwise.
	// Iterator is exhausted when the Seek returns false.
	Seek(t int64) bool
	// At returns the current timestamp/value pair.
	// Before the iterator has advanced At behaviour is unspecified.
	At() (int64, float64)
	// Err returns the current error. It should be used only after iterator is
	// exhausted, that is `Next` or `Seek` returns false.
	Err() error
}

// NewNopIterator returns a new chunk iterator that does not hold any data.
func NewNopIterator() Iterator {
	return nopIterator{}
}

type nopIterator struct{}

func (nopIterator) Seek(int64) bool      { return false }
func (nopIterator) At() (int64, float64) { return math.MinInt64, 0 }
func (nopIterator) Next() bool           { return false }
func (nopIterator) Err() error           { return nil }

// Pool is used to create and reuse chunk references to avoid allocations.
type Pool interface {
	Put(Chunk) error
	Get(e Encoding, b []byte) (Chunk, error)
}

// pool is a memory pool of chunk objects.
type pool struct {
	xor sync.Pool
}

// NewPool returns a new pool.
func NewPool() Pool {
	return &pool{
		xor: sync.Pool{
			New: func() interface{} {
				return &XORChunk{b: bstream{}}
			},
		},
	}
}

func (p *pool) Get(e Encoding, b []byte) (Chunk, error) {
	switch e {
	case EncXOR:
		c := p.xor.Get().(*XORChunk)
		c.b.stream = b
		c.b.count = 0
		return c, nil
	}
	return nil, errors.Errorf("invalid encoding %q", e)
}

func (p *pool) Put(c Chunk) error {
	switch c.Encoding() {
	case EncXOR:
		xc, ok := c.(*XORChunk)
		// This may happen often with wrapped chunks. Nothing we can really do about
		// it but returning an error would cause a lot of allocations again. Thus,
		// we just skip it.
		if !ok {
			return nil
		}
		xc.b.stream = nil
		xc.b.count = 0
		p.xor.Put(c)
	default:
		return errors.Errorf("invalid encoding %q", c.Encoding())
	}
	return nil
}

// FromData returns a chunk from a byte slice of chunk data.
// This is there so that users of the library can easily create chunks from
// bytes.
func FromData(e Encoding, d []byte) (Chunk, error) {
	switch e {
	case EncXOR:
		return &XORChunk{b: bstream{count: 0, stream: d}}, nil
	}
	return nil, fmt.Errorf("unknown chunk encoding: %d", e)
}
<commit_msg>Specify that returned samples must be ordered by timestamp. (#6877)<commit_after>// Copyright 2017 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package chunkenc

import (
	"fmt"
	"math"
	"sync"

	"github.com/pkg/errors"
)

// Encoding is the identifier for a chunk encoding.
type Encoding uint8

func (e Encoding) String() string {
	switch e {
	case EncNone:
		return "none"
	case EncXOR:
		return "XOR"
	}
	return "<unknown>"
}

// The different available chunk encodings.
const (
	EncNone Encoding = iota
	EncXOR
)

// Chunk holds a sequence of sample pairs that can be iterated over and appended to.
type Chunk interface {
	// Bytes returns the underlying byte slice of the chunk.
	Bytes() []byte

	// Encoding returns the encoding type of the chunk.
	Encoding() Encoding

	// Appender returns an appender to append samples to the chunk.
	Appender() (Appender, error)

	// The iterator passed as argument is for re-use.
	// Depending on implementation, the iterator can
	// be re-used or a new iterator can be allocated.
	Iterator(Iterator) Iterator

	// NumSamples returns the number of samples in the chunk.
	NumSamples() int

	// Compact is called whenever a chunk is expected to be complete (no more
	// samples appended) and the underlying implementation can eventually
	// optimize the chunk.
	// There's no strong guarantee that no samples will be appended once
	// Compact() is called. Implementing this function is optional.
	Compact()
}

// Appender adds sample pairs to a chunk.
type Appender interface {
	Append(int64, float64)
}

// Iterator is a simple iterator that can only get the next value.
// Iterator iterates over the samples of a time series, in timestamp-increasing order.
type Iterator interface {
	// Next advances the iterator by one.
	Next() bool
	// Seek advances the iterator forward to the first sample with the timestamp equal or greater than t.
	// If current sample found by previous `Next` or `Seek` operation already has this property, Seek has no effect.
	// Seek returns true, if such sample exists, false otherwise.
	// Iterator is exhausted when the Seek returns false.
	Seek(t int64) bool
	// At returns the current timestamp/value pair.
	// Before the iterator has advanced At behaviour is unspecified.
	At() (int64, float64)
	// Err returns the current error. It should be used only after iterator is
	// exhausted, that is `Next` or `Seek` returns false.
	Err() error
}

// NewNopIterator returns a new chunk iterator that does not hold any data.
func NewNopIterator() Iterator {
	return nopIterator{}
}

type nopIterator struct{}

func (nopIterator) Seek(int64) bool      { return false }
func (nopIterator) At() (int64, float64) { return math.MinInt64, 0 }
func (nopIterator) Next() bool           { return false }
func (nopIterator) Err() error           { return nil }

// Pool is used to create and reuse chunk references to avoid allocations.
type Pool interface {
	Put(Chunk) error
	Get(e Encoding, b []byte) (Chunk, error)
}

// pool is a memory pool of chunk objects.
type pool struct {
	xor sync.Pool
}

// NewPool returns a new pool.
func NewPool() Pool {
	return &pool{
		xor: sync.Pool{
			New: func() interface{} {
				return &XORChunk{b: bstream{}}
			},
		},
	}
}

func (p *pool) Get(e Encoding, b []byte) (Chunk, error) {
	switch e {
	case EncXOR:
		c := p.xor.Get().(*XORChunk)
		c.b.stream = b
		c.b.count = 0
		return c, nil
	}
	return nil, errors.Errorf("invalid encoding %q", e)
}

func (p *pool) Put(c Chunk) error {
	switch c.Encoding() {
	case EncXOR:
		xc, ok := c.(*XORChunk)
		// This may happen often with wrapped chunks. Nothing we can really do about
		// it but returning an error would cause a lot of allocations again. Thus,
		// we just skip it.
		if !ok {
			return nil
		}
		xc.b.stream = nil
		xc.b.count = 0
		p.xor.Put(c)
	default:
		return errors.Errorf("invalid encoding %q", c.Encoding())
	}
	return nil
}

// FromData returns a chunk from a byte slice of chunk data.
// This is there so that users of the library can easily create chunks from
// bytes.
func FromData(e Encoding, d []byte) (Chunk, error) {
	switch e {
	case EncXOR:
		return &XORChunk{b: bstream{count: 0, stream: d}}, nil
	}
	return nil, fmt.Errorf("unknown chunk encoding: %d", e)
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	"github.com/koding/bongo"
)

const (
	Permission_ROLE_SUPERADMIN = "superadmin"
	Permission_ROLE_ADMIN      = "admin"
	Permission_ROLE_MODERATOR  = "moderator"
	Permission_ROLE_MEMBER     = "member"
	Permission_ROLE_GUEST      = "guest"
)

const (
	Permission_STATUS_ALLOWED    = "allowed"
	Permission_STATUS_DISALLOWED = "disallowed"
)

type Permission struct {
	// unique identifier of the channel
	Id int64 `json:"id,string"`

	// name of the permission
	Name string `json:"name"`

	// Id of the channel
	ChannelId int64 `json:"channelId,string"       sql:"NOT NULL"`

	// admin, moderator, member, guest
	RoleConstant string `json:"roleConstant"`

	// Status of the permission in the channel
	// Allowed/Disallowed
	StatusConstant string `json:"statusConstant"   sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of permission
	CreatedAt time.Time `json:"createdAt"          sql:"NOT NULL"`

	// Modification date of the permission
	UpdatedAt time.Time `json:"updatedAt"          sql:"NOT NULL"`
}

func NewPermission() *Permission {
	return &Permission{}
}

func (p *Permission) FetchStatus() (string, error) {
	if p.ChannelId == 0 {
		// todo return default permission
		return Permission_STATUS_ALLOWED, nil
	}

	if p.RoleConstant == "" {
		// implicitly set role as guest, if not set
		p.RoleConstant = Permission_ROLE_GUEST
	}

	selector := map[string]interface{}{
		"channel_id":    p.ChannelId,
		"role_constant": p.RoleConstant,
	}

	err := p.One(bongo.NewQS(selector))
	if err != nil && err != bongo.RecordNotFound {
		return "", err
	}

	if err == bongo.RecordNotFound {
		// todo return default permission
		return Permission_STATUS_ALLOWED, nil
	}

	return p.StatusConstant, nil
}

func (p *Permission) EnsureAllowance() error {
	status, err := p.FetchStatus()
	if err != nil {
		return err
	}

	if status == Permission_STATUS_ALLOWED {
		return nil
	}

	return ErrAccessDenied
}
<commit_msg>Social: added permission  response struct<commit_after>package models

import (
	"time"

	"github.com/koding/bongo"
)

const (
	Permission_ROLE_SUPERADMIN = "superadmin"
	Permission_ROLE_ADMIN      = "admin"
	Permission_ROLE_MODERATOR  = "moderator"
	Permission_ROLE_MEMBER     = "member"
	Permission_ROLE_GUEST      = "guest"
)

const (
	Permission_STATUS_ALLOWED    = "allowed"
	Permission_STATUS_DISALLOWED = "disallowed"
)

type PermissionResponse struct {
	Defaults []*Permission
	Context  []*Permission
}

type Permission struct {
	// unique identifier of the channel
	Id int64 `json:"id,string"`

	// name of the permission
	Name string `json:"name"`

	// Id of the channel
	ChannelId int64 `json:"channelId,string"       sql:"NOT NULL"`

	// admin, moderator, member, guest
	RoleConstant string `json:"roleConstant"`

	// Status of the permission in the channel
	// Allowed/Disallowed
	StatusConstant string `json:"statusConstant"   sql:"NOT NULL;TYPE:VARCHAR(100);"`

	// Creation date of permission
	CreatedAt time.Time `json:"createdAt"          sql:"NOT NULL"`

	// Modification date of the permission
	UpdatedAt time.Time `json:"updatedAt"          sql:"NOT NULL"`
}

func NewPermission() *Permission {
	return &Permission{}
}

func (p *Permission) FetchStatus() (string, error) {
	if p.ChannelId == 0 {
		// todo return default permission
		return Permission_STATUS_ALLOWED, nil
	}

	if p.RoleConstant == "" {
		// implicitly set role as guest, if not set
		p.RoleConstant = Permission_ROLE_GUEST
	}

	selector := map[string]interface{}{
		"channel_id":    p.ChannelId,
		"role_constant": p.RoleConstant,
	}

	err := p.One(bongo.NewQS(selector))
	if err != nil && err != bongo.RecordNotFound {
		return "", err
	}

	if err == bongo.RecordNotFound {
		// todo return default permission
		return Permission_STATUS_ALLOWED, nil
	}

	return p.StatusConstant, nil
}

func (p *Permission) EnsureAllowance() error {
	status, err := p.FetchStatus()
	if err != nil {
		return err
	}

	if status == Permission_STATUS_ALLOWED {
		return nil
	}

	return ErrAccessDenied
}
<|endoftext|>