<commit_before>package vault

import (
	"context"
	"fmt"
	"reflect"
	"strings"

	"github.com/armon/go-radix"
	"github.com/hashicorp/errwrap"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/hashicorp/vault/helper/identity"
	"github.com/hashicorp/vault/helper/strutil"
	"github.com/hashicorp/vault/logical"
)

// ACL is used to wrap a set of policies to provide
// an efficient interface for access control.
type ACL struct {
	// exactRules contains the path policies that are exact
	exactRules *radix.Tree

	// globRules contains the path policies that glob
	globRules *radix.Tree

	// root is enabled if the "root" named policy is present.
	root bool
}

type PolicyCheckOpts struct {
	RootPrivsRequired bool
	Unauth            bool
}

type AuthResults struct {
	ACLResults *ACLResults
	Allowed    bool
	RootPrivs  bool
	Error      *multierror.Error
}

type ACLResults struct {
	Allowed    bool
	RootPrivs  bool
	IsRoot     bool
	MFAMethods []string
}

// New is used to construct a policy based ACL from a set of policies.
func NewACL(policies []*Policy) (*ACL, error) {
	// Initialize
	a := &ACL{
		exactRules: radix.New(),
		globRules:  radix.New(),
		root:       false,
	}

	// Inject each policy
	for _, policy := range policies {
		// Ignore a nil policy object
		if policy == nil {
			continue
		}

		switch policy.Type {
		case PolicyTypeACL:
		default:
			return nil, fmt.Errorf("unable to parse policy (wrong type)")
		}

		// Check if this is root
		if policy.Name == "root" {
			a.root = true
		}
		for _, pc := range policy.Paths {
			// Check which tree to use
			tree := a.exactRules
			if pc.Glob {
				tree = a.globRules
			}

			// Check for an existing policy
			raw, ok := tree.Get(pc.Prefix)
			if !ok {
				clonedPerms, err := pc.Permissions.Clone()
				if err != nil {
					return nil, errwrap.Wrapf("error cloning ACL permissions: {{err}}", err)
				}
				tree.Insert(pc.Prefix, clonedPerms)
				continue
			}

			// these are the ones already in the tree
			existingPerms := raw.(*ACLPermissions)

			switch {
			case existingPerms.CapabilitiesBitmap&DenyCapabilityInt > 0:
				// If we are explicitly denied in the existing capability set,
				// don't save anything else
				continue

			case pc.Permissions.CapabilitiesBitmap&DenyCapabilityInt > 0:
				// If this new policy explicitly denies, only save the deny value
				existingPerms.CapabilitiesBitmap = DenyCapabilityInt
				existingPerms.AllowedParameters = nil
				existingPerms.DeniedParameters = nil
				goto INSERT

			default:
				// Insert the capabilities in this new policy into the existing
				// value
				existingPerms.CapabilitiesBitmap = existingPerms.CapabilitiesBitmap | pc.Permissions.CapabilitiesBitmap
			}

			// Note: In these stanzas, we're preferring minimum lifetimes. So
			// we take the lesser of two specified max values, or we take the
			// lesser of two specified min values, the idea being, allowing
			// token lifetime to be minimum possible.
			//
			// If we have an existing max, and we either don't have a current
			// max, or the current is greater than the previous, use the
			// existing.
			if pc.Permissions.MaxWrappingTTL > 0 &&
				(existingPerms.MaxWrappingTTL == 0 ||
					pc.Permissions.MaxWrappingTTL < existingPerms.MaxWrappingTTL) {
				existingPerms.MaxWrappingTTL = pc.Permissions.MaxWrappingTTL
			}
			// If we have an existing min, and we either don't have a current
			// min, or the current is greater than the previous, use the
			// existing
			if pc.Permissions.MinWrappingTTL > 0 &&
				(existingPerms.MinWrappingTTL == 0 ||
					pc.Permissions.MinWrappingTTL < existingPerms.MinWrappingTTL) {
				existingPerms.MinWrappingTTL = pc.Permissions.MinWrappingTTL
			}

			if len(pc.Permissions.AllowedParameters) > 0 {
				if existingPerms.AllowedParameters == nil {
					existingPerms.AllowedParameters = pc.Permissions.AllowedParameters
				} else {
					for key, value := range pc.Permissions.AllowedParameters {
						pcValue, ok := existingPerms.AllowedParameters[key]
						// If an empty array exist it should overwrite any other
						// value.
						if len(value) == 0 || (ok && len(pcValue) == 0) {
							existingPerms.AllowedParameters[key] = []interface{}{}
						} else {
							// Merge the two maps, appending values on key conflict.
							existingPerms.AllowedParameters[key] = append(value, existingPerms.AllowedParameters[key]...)
						}
					}
				}
			}

			if len(pc.Permissions.DeniedParameters) > 0 {
				if existingPerms.DeniedParameters == nil {
					existingPerms.DeniedParameters = pc.Permissions.DeniedParameters
				} else {
					for key, value := range pc.Permissions.DeniedParameters {
						pcValue, ok := existingPerms.DeniedParameters[key]
						// If an empty array exist it should overwrite any other
						// value.
						if len(value) == 0 || (ok && len(pcValue) == 0) {
							existingPerms.DeniedParameters[key] = []interface{}{}
						} else {
							// Merge the two maps, appending values on key conflict.
							existingPerms.DeniedParameters[key] = append(value, existingPerms.DeniedParameters[key]...)
						}
					}
				}
			}

			if len(pc.Permissions.RequiredParameters) > 0 {
				if len(existingPerms.RequiredParameters) == 0 {
					existingPerms.RequiredParameters = pc.Permissions.RequiredParameters
				} else {
					for _, v := range pc.Permissions.RequiredParameters {
						if !strutil.StrListContains(existingPerms.RequiredParameters, v) {
							existingPerms.RequiredParameters = append(existingPerms.RequiredParameters, v)
						}
					}
				}
			}

		INSERT:
			tree.Insert(pc.Prefix, existingPerms)
		}
	}
	return a, nil
}

func (a *ACL) Capabilities(path string) (pathCapabilities []string) {
	// Fast-path root
	if a.root {
		return []string{RootCapability}
	}

	// Find an exact matching rule, look for glob if no match
	var capabilities uint32
	raw, ok := a.exactRules.Get(path)

	if ok {
		perm := raw.(*ACLPermissions)
		capabilities = perm.CapabilitiesBitmap
		goto CHECK
	}

	// Find a glob rule, default deny if no match
	_, raw, ok = a.globRules.LongestPrefix(path)
	if !ok {
		return []string{DenyCapability}
	} else {
		perm := raw.(*ACLPermissions)
		capabilities = perm.CapabilitiesBitmap
	}

CHECK:
	if capabilities&SudoCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, SudoCapability)
	}
	if capabilities&ReadCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, ReadCapability)
	}
	if capabilities&ListCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, ListCapability)
	}
	if capabilities&UpdateCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, UpdateCapability)
	}
	if capabilities&DeleteCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, DeleteCapability)
	}
	if capabilities&CreateCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, CreateCapability)
	}

	// If "deny" is explicitly set or if the path has no capabilities at all,
	// set the path capabilities to "deny"
	if capabilities&DenyCapabilityInt > 0 || len(pathCapabilities) == 0 {
		pathCapabilities = []string{DenyCapability}
	}
	return
}

// AllowOperation is used to check if the given operation is permitted.
func (a *ACL) AllowOperation(req *logical.Request) (ret *ACLResults) {
	ret = new(ACLResults)

	// Fast-path root
	if a.root {
		ret.Allowed = true
		ret.RootPrivs = true
		ret.IsRoot = true
		return
	}
	op := req.Operation
	path := req.Path

	// Help is always allowed
	if op == logical.HelpOperation {
		ret.Allowed = true
		return
	}

	var permissions *ACLPermissions

	// Find an exact matching rule, look for glob if no match
	var capabilities uint32
	raw, ok := a.exactRules.Get(path)
	if ok {
		permissions = raw.(*ACLPermissions)
		capabilities = permissions.CapabilitiesBitmap
		goto CHECK
	}

	// Find a glob rule, default deny if no match
	_, raw, ok = a.globRules.LongestPrefix(path)
	if !ok {
		return
	} else {
		permissions = raw.(*ACLPermissions)
		capabilities = permissions.CapabilitiesBitmap
	}

CHECK:
	// Check if the minimum permissions are met
	// If "deny" has been explicitly set, only deny will be in the map, so we
	// only need to check for the existence of other values
	ret.RootPrivs = capabilities&SudoCapabilityInt > 0

	operationAllowed := false
	switch op {
	case logical.ReadOperation:
		operationAllowed = capabilities&ReadCapabilityInt > 0
	case logical.ListOperation:
		operationAllowed = capabilities&ListCapabilityInt > 0
	case logical.UpdateOperation:
		operationAllowed = capabilities&UpdateCapabilityInt > 0
	case logical.DeleteOperation:
		operationAllowed = capabilities&DeleteCapabilityInt > 0
	case logical.CreateOperation:
		operationAllowed = capabilities&CreateCapabilityInt > 0

	// These three re-use UpdateCapabilityInt since that's the most appropriate
	// capability/operation mapping
	case logical.RevokeOperation, logical.RenewOperation, logical.RollbackOperation:
		operationAllowed = capabilities&UpdateCapabilityInt > 0

	default:
		return
	}

	if !operationAllowed {
		return
	}

	if permissions.MaxWrappingTTL > 0 {
		if req.WrapInfo == nil || req.WrapInfo.TTL > permissions.MaxWrappingTTL {
			return
		}
	}
	if permissions.MinWrappingTTL > 0 {
		if req.WrapInfo == nil || req.WrapInfo.TTL < permissions.MinWrappingTTL {
			return
		}
	}
	// This situation can happen because of merging, even though in a single
	// path statement we check on ingress
	if permissions.MinWrappingTTL != 0 &&
		permissions.MaxWrappingTTL != 0 &&
		permissions.MaxWrappingTTL < permissions.MinWrappingTTL {
		return
	}

	// Only check parameter permissions for operations that can modify
	// parameters.
	if op == logical.ReadOperation || op == logical.UpdateOperation || op == logical.CreateOperation {
		for _, parameter := range permissions.RequiredParameters {
			if _, ok := req.Data[strings.ToLower(parameter)]; !ok {
				return
			}
		}

		// If there are no data fields, allow
		if len(req.Data) == 0 {
			ret.Allowed = true
			return
		}

		if len(permissions.DeniedParameters) == 0 {
			goto ALLOWED_PARAMETERS
		}

		// Check if all parameters have been denied
		if _, ok := permissions.DeniedParameters["*"]; ok {
			return
		}

		for parameter, value := range req.Data {
			// Check if parameter has been explicitly denied
			if valueSlice, ok := permissions.DeniedParameters[strings.ToLower(parameter)]; ok {
				// If the value exists in denied values slice, deny
				if valueInParameterList(value, valueSlice) {
					return
				}
			}
		}

	ALLOWED_PARAMETERS:
		// If we don't have any allowed parameters set, allow
		if len(permissions.AllowedParameters) == 0 {
			ret.Allowed = true
			return
		}

		_, allowedAll := permissions.AllowedParameters["*"]
		if len(permissions.AllowedParameters) == 1 && allowedAll {
			ret.Allowed = true
			return
		}

		for parameter, value := range req.Data {
			valueSlice, ok := permissions.AllowedParameters[strings.ToLower(parameter)]
			// Requested parameter is not in allowed list
			if !ok && !allowedAll {
				return
			}

			// If the value doesn't exists in the allowed values slice,
			// deny
			if ok && !valueInParameterList(value, valueSlice) {
				return
			}
		}
	}

	ret.Allowed = true
	return
}
func (c *Core) performPolicyChecks(ctx context.Context, acl *ACL, te *TokenEntry, req *logical.Request, inEntity *identity.Entity, opts *PolicyCheckOpts) (ret *AuthResults) {
	ret = new(AuthResults)

	// First, perform normal ACL checks if requested. The only time no ACL
	// should be applied is if we are only processing EGPs against a login
	// path in which case opts.Unauth will be set.
	if acl != nil && !opts.Unauth {
		ret.ACLResults = acl.AllowOperation(req)
		ret.RootPrivs = ret.ACLResults.RootPrivs
		// Root is always allowed; skip Sentinel/MFA checks
		if ret.ACLResults.IsRoot {
			//c.logger.Warn("policy: token is root, skipping checks")
			ret.Allowed = true
			return
		}
		if !ret.ACLResults.Allowed {
			return
		}
		if !ret.RootPrivs && opts.RootPrivsRequired {
			return
		}
	}

	ret.Allowed = true
	return
}

func valueInParameterList(v interface{}, list []interface{}) bool {
	// Empty list is equivalent to the item always existing in the list
	if len(list) == 0 {
		return true
	}

	return valueInSlice(v, list)
}

func valueInSlice(v interface{}, list []interface{}) bool {
	for _, el := range list {
		if reflect.TypeOf(el).String() == "string" && reflect.TypeOf(v).String() == "string" {
			item := el.(string)
			val := v.(string)

			if strutil.GlobbedStringsMatch(item, val) {
				return true
			}
		} else if reflect.DeepEqual(el, v) {
			return true
		}
	}

	return false
}
<commit_msg>Use copystructure when assigning allowed/denied params from nil check (#4585)<commit_after>package vault

import (
	"context"
	"fmt"
	"reflect"
	"strings"

	"github.com/armon/go-radix"
	"github.com/hashicorp/errwrap"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/hashicorp/vault/helper/identity"
	"github.com/hashicorp/vault/helper/strutil"
	"github.com/hashicorp/vault/logical"
	"github.com/mitchellh/copystructure"
)

// ACL is used to wrap a set of policies to provide
// an efficient interface for access control.
type ACL struct {
	// exactRules contains the path policies that are exact
	exactRules *radix.Tree

	// globRules contains the path policies that glob
	globRules *radix.Tree

	// root is enabled if the "root" named policy is present.
	root bool
}

type PolicyCheckOpts struct {
	RootPrivsRequired bool
	Unauth            bool
}

type AuthResults struct {
	ACLResults *ACLResults
	Allowed    bool
	RootPrivs  bool
	Error      *multierror.Error
}

type ACLResults struct {
	Allowed    bool
	RootPrivs  bool
	IsRoot     bool
	MFAMethods []string
}

// New is used to construct a policy based ACL from a set of policies.
func NewACL(policies []*Policy) (*ACL, error) {
	// Initialize
	a := &ACL{
		exactRules: radix.New(),
		globRules:  radix.New(),
		root:       false,
	}

	// Inject each policy
	for _, policy := range policies {
		// Ignore a nil policy object
		if policy == nil {
			continue
		}

		switch policy.Type {
		case PolicyTypeACL:
		default:
			return nil, fmt.Errorf("unable to parse policy (wrong type)")
		}

		// Check if this is root
		if policy.Name == "root" {
			a.root = true
		}
		for _, pc := range policy.Paths {
			// Check which tree to use
			tree := a.exactRules
			if pc.Glob {
				tree = a.globRules
			}

			// Check for an existing policy
			raw, ok := tree.Get(pc.Prefix)
			if !ok {
				clonedPerms, err := pc.Permissions.Clone()
				if err != nil {
					return nil, errwrap.Wrapf("error cloning ACL permissions: {{err}}", err)
				}
				tree.Insert(pc.Prefix, clonedPerms)
				continue
			}

			// these are the ones already in the tree
			existingPerms := raw.(*ACLPermissions)

			switch {
			case existingPerms.CapabilitiesBitmap&DenyCapabilityInt > 0:
				// If we are explicitly denied in the existing capability set,
				// don't save anything else
				continue

			case pc.Permissions.CapabilitiesBitmap&DenyCapabilityInt > 0:
				// If this new policy explicitly denies, only save the deny value
				existingPerms.CapabilitiesBitmap = DenyCapabilityInt
				existingPerms.AllowedParameters = nil
				existingPerms.DeniedParameters = nil
				goto INSERT

			default:
				// Insert the capabilities in this new policy into the existing
				// value
				existingPerms.CapabilitiesBitmap = existingPerms.CapabilitiesBitmap | pc.Permissions.CapabilitiesBitmap
			}

			// Note: In these stanzas, we're preferring minimum lifetimes. So
			// we take the lesser of two specified max values, or we take the
			// lesser of two specified min values, the idea being, allowing
			// token lifetime to be minimum possible.
			//
			// If we have an existing max, and we either don't have a current
			// max, or the current is greater than the previous, use the
			// existing.
			if pc.Permissions.MaxWrappingTTL > 0 &&
				(existingPerms.MaxWrappingTTL == 0 ||
					pc.Permissions.MaxWrappingTTL < existingPerms.MaxWrappingTTL) {
				existingPerms.MaxWrappingTTL = pc.Permissions.MaxWrappingTTL
			}
			// If we have an existing min, and we either don't have a current
			// min, or the current is greater than the previous, use the
			// existing
			if pc.Permissions.MinWrappingTTL > 0 &&
				(existingPerms.MinWrappingTTL == 0 ||
					pc.Permissions.MinWrappingTTL < existingPerms.MinWrappingTTL) {
				existingPerms.MinWrappingTTL = pc.Permissions.MinWrappingTTL
			}

			if len(pc.Permissions.AllowedParameters) > 0 {
				if existingPerms.AllowedParameters == nil {
					clonedAllowed, err := copystructure.Copy(pc.Permissions.AllowedParameters)
					if err != nil {
						return nil, err
					}
					existingPerms.AllowedParameters = clonedAllowed.(map[string][]interface{})
				} else {
					for key, value := range pc.Permissions.AllowedParameters {
						pcValue, ok := existingPerms.AllowedParameters[key]
						// If an empty array exist it should overwrite any other
						// value.
						if len(value) == 0 || (ok && len(pcValue) == 0) {
							existingPerms.AllowedParameters[key] = []interface{}{}
						} else {
							// Merge the two maps, appending values on key conflict.
							existingPerms.AllowedParameters[key] = append(value, existingPerms.AllowedParameters[key]...)
						}
					}
				}
			}

			if len(pc.Permissions.DeniedParameters) > 0 {
				if existingPerms.DeniedParameters == nil {
					clonedDenied, err := copystructure.Copy(pc.Permissions.DeniedParameters)
					if err != nil {
						return nil, err
					}
					existingPerms.DeniedParameters = clonedDenied.(map[string][]interface{})
				} else {
					for key, value := range pc.Permissions.DeniedParameters {
						pcValue, ok := existingPerms.DeniedParameters[key]
						// If an empty array exist it should overwrite any other
						// value.
						if len(value) == 0 || (ok && len(pcValue) == 0) {
							existingPerms.DeniedParameters[key] = []interface{}{}
						} else {
							// Merge the two maps, appending values on key conflict.
							existingPerms.DeniedParameters[key] = append(value, existingPerms.DeniedParameters[key]...)
						}
					}
				}
			}

			if len(pc.Permissions.RequiredParameters) > 0 {
				if len(existingPerms.RequiredParameters) == 0 {
					existingPerms.RequiredParameters = pc.Permissions.RequiredParameters
				} else {
					for _, v := range pc.Permissions.RequiredParameters {
						if !strutil.StrListContains(existingPerms.RequiredParameters, v) {
							existingPerms.RequiredParameters = append(existingPerms.RequiredParameters, v)
						}
					}
				}
			}

		INSERT:
			tree.Insert(pc.Prefix, existingPerms)
		}
	}
	return a, nil
}

func (a *ACL) Capabilities(path string) (pathCapabilities []string) {
	// Fast-path root
	if a.root {
		return []string{RootCapability}
	}

	// Find an exact matching rule, look for glob if no match
	var capabilities uint32
	raw, ok := a.exactRules.Get(path)

	if ok {
		perm := raw.(*ACLPermissions)
		capabilities = perm.CapabilitiesBitmap
		goto CHECK
	}

	// Find a glob rule, default deny if no match
	_, raw, ok = a.globRules.LongestPrefix(path)
	if !ok {
		return []string{DenyCapability}
	} else {
		perm := raw.(*ACLPermissions)
		capabilities = perm.CapabilitiesBitmap
	}

CHECK:
	if capabilities&SudoCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, SudoCapability)
	}
	if capabilities&ReadCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, ReadCapability)
	}
	if capabilities&ListCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, ListCapability)
	}
	if capabilities&UpdateCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, UpdateCapability)
	}
	if capabilities&DeleteCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, DeleteCapability)
	}
	if capabilities&CreateCapabilityInt > 0 {
		pathCapabilities = append(pathCapabilities, CreateCapability)
	}

	// If "deny" is explicitly set or if the path has no capabilities at all,
	// set the path capabilities to "deny"
	if capabilities&DenyCapabilityInt > 0 || len(pathCapabilities) == 0 {
		pathCapabilities = []string{DenyCapability}
	}
	return
}

// AllowOperation is used to check if the given operation is permitted.
func (a *ACL) AllowOperation(req *logical.Request) (ret *ACLResults) {
	ret = new(ACLResults)

	// Fast-path root
	if a.root {
		ret.Allowed = true
		ret.RootPrivs = true
		ret.IsRoot = true
		return
	}
	op := req.Operation
	path := req.Path

	// Help is always allowed
	if op == logical.HelpOperation {
		ret.Allowed = true
		return
	}

	var permissions *ACLPermissions

	// Find an exact matching rule, look for glob if no match
	var capabilities uint32
	raw, ok := a.exactRules.Get(path)
	if ok {
		permissions = raw.(*ACLPermissions)
		capabilities = permissions.CapabilitiesBitmap
		goto CHECK
	}

	// Find a glob rule, default deny if no match
	_, raw, ok = a.globRules.LongestPrefix(path)
	if !ok {
		return
	} else {
		permissions = raw.(*ACLPermissions)
		capabilities = permissions.CapabilitiesBitmap
	}

CHECK:
	// Check if the minimum permissions are met
	// If "deny" has been explicitly set, only deny will be in the map, so we
	// only need to check for the existence of other values
	ret.RootPrivs = capabilities&SudoCapabilityInt > 0

	operationAllowed := false
	switch op {
	case logical.ReadOperation:
		operationAllowed = capabilities&ReadCapabilityInt > 0
	case logical.ListOperation:
		operationAllowed = capabilities&ListCapabilityInt > 0
	case logical.UpdateOperation:
		operationAllowed = capabilities&UpdateCapabilityInt > 0
	case logical.DeleteOperation:
		operationAllowed = capabilities&DeleteCapabilityInt > 0
	case logical.CreateOperation:
		operationAllowed = capabilities&CreateCapabilityInt > 0

	// These three re-use UpdateCapabilityInt since that's the most appropriate
	// capability/operation mapping
	case logical.RevokeOperation, logical.RenewOperation, logical.RollbackOperation:
		operationAllowed = capabilities&UpdateCapabilityInt > 0

	default:
		return
	}

	if !operationAllowed {
		return
	}

	if permissions.MaxWrappingTTL > 0 {
		if req.WrapInfo == nil || req.WrapInfo.TTL > permissions.MaxWrappingTTL {
			return
		}
	}
	if permissions.MinWrappingTTL > 0 {
		if req.WrapInfo == nil || req.WrapInfo.TTL < permissions.MinWrappingTTL {
			return
		}
	}
	// This situation can happen because of merging, even though in a single
	// path statement we check on ingress
	if permissions.MinWrappingTTL != 0 &&
		permissions.MaxWrappingTTL != 0 &&
		permissions.MaxWrappingTTL < permissions.MinWrappingTTL {
		return
	}

	// Only check parameter permissions for operations that can modify
	// parameters.
	if op == logical.ReadOperation || op == logical.UpdateOperation || op == logical.CreateOperation {
		for _, parameter := range permissions.RequiredParameters {
			if _, ok := req.Data[strings.ToLower(parameter)]; !ok {
				return
			}
		}

		// If there are no data fields, allow
		if len(req.Data) == 0 {
			ret.Allowed = true
			return
		}

		if len(permissions.DeniedParameters) == 0 {
			goto ALLOWED_PARAMETERS
		}

		// Check if all parameters have been denied
		if _, ok := permissions.DeniedParameters["*"]; ok {
			return
		}

		for parameter, value := range req.Data {
			// Check if parameter has been explicitly denied
			if valueSlice, ok := permissions.DeniedParameters[strings.ToLower(parameter)]; ok {
				// If the value exists in denied values slice, deny
				if valueInParameterList(value, valueSlice) {
					return
				}
			}
		}

	ALLOWED_PARAMETERS:
		// If we don't have any allowed parameters set, allow
		if len(permissions.AllowedParameters) == 0 {
			ret.Allowed = true
			return
		}

		_, allowedAll := permissions.AllowedParameters["*"]
		if len(permissions.AllowedParameters) == 1 && allowedAll {
			ret.Allowed = true
			return
		}

		for parameter, value := range req.Data {
			valueSlice, ok := permissions.AllowedParameters[strings.ToLower(parameter)]
			// Requested parameter is not in allowed list
			if !ok && !allowedAll {
				return
			}

			// If the value doesn't exists in the allowed values slice,
			// deny
			if ok && !valueInParameterList(value, valueSlice) {
				return
			}
		}
	}

	ret.Allowed = true
	return
}
func (c *Core) performPolicyChecks(ctx context.Context, acl *ACL, te *TokenEntry, req *logical.Request, inEntity *identity.Entity, opts *PolicyCheckOpts) (ret *AuthResults) {
	ret = new(AuthResults)

	// First, perform normal ACL checks if requested. The only time no ACL
	// should be applied is if we are only processing EGPs against a login
	// path in which case opts.Unauth will be set.
	if acl != nil && !opts.Unauth {
		ret.ACLResults = acl.AllowOperation(req)
		ret.RootPrivs = ret.ACLResults.RootPrivs
		// Root is always allowed; skip Sentinel/MFA checks
		if ret.ACLResults.IsRoot {
			//c.logger.Warn("policy: token is root, skipping checks")
			ret.Allowed = true
			return
		}
		if !ret.ACLResults.Allowed {
			return
		}
		if !ret.RootPrivs && opts.RootPrivsRequired {
			return
		}
	}

	ret.Allowed = true
	return
}

func valueInParameterList(v interface{}, list []interface{}) bool {
	// Empty list is equivalent to the item always existing in the list
	if len(list) == 0 {
		return true
	}

	return valueInSlice(v, list)
}

func valueInSlice(v interface{}, list []interface{}) bool {
	for _, el := range list {
		if reflect.TypeOf(el).String() == "string" && reflect.TypeOf(v).String() == "string" {
			item := el.(string)
			val := v.(string)

			if strutil.GlobbedStringsMatch(item, val) {
				return true
			}
		} else if reflect.DeepEqual(el, v) {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package vcl

import (
	"reflect"

	"fmt"

	"github.com/ying32/govcl/vcl/api"
	"github.com/ying32/govcl/vcl/rtl"
	. "github.com/ying32/govcl/vcl/types"
)

// ShowMessage 显示一个消息框
func ShowMessage(msg string) {
	api.DShowMessage(msg)
}

func ShowMessageFmt(format string, args ...interface{}) {
	ShowMessage(fmt.Sprintf(format, args...))
}

// MessageDlg 消息框，Buttons为按钮样式，祥见types.TMsgDlgButtons
func MessageDlg(Msg string, DlgType TMsgDlgType, Buttons ...uint8) int32 {
	return api.DMessageDlg(Msg, DlgType, TMsgDlgButtons(rtl.Include(0, Buttons...)), 0)
}

// CheckPtr 检测接口是否被实例化，如果已经实例化则返回实例指针
func CheckPtr(value IObject) uintptr {
	if value == nil || reflect.ValueOf(value).Pointer() == 0 {
		return 0
	}
	return value.Instance()
}

// SelectDirectory1 选择目录
func SelectDirectory1(options TSelectDirOpts) (bool, string) {
	return api.DSelectDirectory1(options)
}

// SelectDirectory2 选择目录，一般 options默认是SdNewUI，parent默认为nil
func SelectDirectory2(caption, root string, options TSelectDirExtOpts, parent IObject) (bool, string) {
	return api.DSelectDirectory2(caption, root, options, CheckPtr(parent))
}

// SelectDirectory3 选择目录， options默认是SdNewUI，parent默认为nil
func SelectDirectory3(caption, root string, options ...uint8) (bool, string) {
	opts := rtl.Include(0, options...)
	if len(options) == 0 {
		opts = rtl.Include(opts, SdNewUI)
	}
	return SelectDirectory2(caption, root, opts, nil)
}

// ThreadSync 主线程中执行
func ThreadSync(fn TThreadProc) {
	api.DSynchronize(fn, 1)
}

// ThreadSyncVCL 主线程中执行，第二个参数决定是否使用Delphi自带的，此也只对libvcl生效，1使用消息，0使用Delphi自带的线程同步方法。
func ThreadSyncVCL(fn TThreadProc) {
	api.DSynchronize(fn, 0)
}

// InputBox 输入框
func InputBox(aCaption, aPrompt, aDefault string) string {
	return api.DInputBox(aCaption, aPrompt, aDefault)
}

// InputQuery 输入框
func InputQuery(aCaption, aPrompt string, value *string) bool {
	return api.DInputQuery(aCaption, aPrompt, value)
}
<commit_msg>rename<commit_after>package vcl

import (
	"reflect"

	"fmt"

	"github.com/ying32/govcl/vcl/api"
	"github.com/ying32/govcl/vcl/rtl"
	. "github.com/ying32/govcl/vcl/types"
)

// ShowMessage 显示一个消息框
func ShowMessage(msg string) {
	api.DShowMessage(msg)
}

func ShowMessageFmt(format string, args ...interface{}) {
	ShowMessage(fmt.Sprintf(format, args...))
}

// MessageDlg 消息框，Buttons为按钮样式，祥见types.TMsgDlgButtons
func MessageDlg(Msg string, DlgType TMsgDlgType, Buttons ...uint8) int32 {
	return api.DMessageDlg(Msg, DlgType, TMsgDlgButtons(rtl.Include(0, Buttons...)), 0)
}

// CheckPtr 检测接口是否被实例化，如果已经实例化则返回实例指针
func CheckPtr(value IObject) uintptr {
	if value == nil || reflect.ValueOf(value).Pointer() == 0 {
		return 0
	}
	return value.Instance()
}

// SelectDirectory1 选择目录
func SelectDirectory1(options TSelectDirOpts) (bool, string) {
	return api.DSelectDirectory1(options)
}

// SelectDirectory2 选择目录，一般 options默认是SdNewUI，parent默认为nil
func SelectDirectory2(caption, root string, options TSelectDirExtOpts, parent IObject) (bool, string) {
	return api.DSelectDirectory2(caption, root, options, CheckPtr(parent))
}

// SelectDirectory3 选择目录， options默认是SdNewUI，parent默认为nil
func SelectDirectory3(caption, root string, options ...uint8) (bool, string) {
	opts := rtl.Include(0, options...)
	if len(options) == 0 {
		opts = rtl.Include(opts, SdNewUI)
	}
	return SelectDirectory2(caption, root, opts, nil)
}

// ThreadSync 主线程中执行
func ThreadSync(fn TThreadProc) {
	api.DSynchronize(fn, 1)
}

// ThreadSyncVcl 主线程中执行，第二个参数决定是否使用Delphi自带的，此也只对libvcl生效，1使用消息，0使用Delphi自带的线程同步方法。
func ThreadSyncVcl(fn TThreadProc) {
	api.DSynchronize(fn, 0)
}

// InputBox 输入框
func InputBox(aCaption, aPrompt, aDefault string) string {
	return api.DInputBox(aCaption, aPrompt, aDefault)
}

// InputQuery 输入框
func InputQuery(aCaption, aPrompt string, value *string) bool {
	return api.DInputQuery(aCaption, aPrompt, value)
}
<|endoftext|>


<commit_before>package libkb

import (
	"encoding/hex"
	"testing"
)

type dktest struct {
	name       string
	passphrase string
	salt       string
	pwh        string
	ekey       string
	dkey       string
	lkey       string
}

var dktests = []dktest{
	{
		name:       "simple 1",
		passphrase: "my passphrase",
		salt:       "random salt zxcv",
		pwh:        "eb92a4bc72cbce98b80c5bfb391b353c37af5f3398e52c7cb436b73de97abc48",
		ekey:       "248f73a3645486c7d2c327da2bb7bb3129cf2347494d54ca9e35083f325ab57e",
		dkey:       "b4f8ffced85c240c4833afac527ce1f9be37e2e645fc020d41c31d8179ff7ef2",
		lkey:       "daf502dcc05af15e8c9b4c36a195566e1cfdd5dbb7593e268e0053e89ed74c0f",
	},
	{
		name:       "simple 2",
		passphrase: "my passphrase",
		salt:       "random salt qwer",
		pwh:        "5c44c619c7f29bc446af06ecd2d5c8d0a58db04970891aa18084fac8014c717a",
		ekey:       "c93b7470701a0623a062b849f0527b68faa568549926b77320e9030b12d29197",
		dkey:       "77375b7d2e59b9fdd4733dbead5ba66116cec508e05919b98332b97e437a75b0",
		lkey:       "5fec13005cca4f12ce0b624db02488d9ea4e5ef54b67649620c810e6b4e01376",
	},
	{
		name:       "simple 3",
		passphrase: "my passphrase is longer",
		salt:       "random salt zxcv",
		pwh:        "de526ef302f50283d0a0aecdc303e1f42c3b206060657bf03f781f076eec1459",
		ekey:       "199c9e10e5c9505c431cd2d3235873e7e113918511178afc341d28d48f1b5dd7",
		dkey:       "1c2c2d040fc743f939b1af6e90dde1a0e9181e2d6a0e584c75f7d363100493d5",
		lkey:       "66647f005c89d55efbc4683b2a86210d545d77e6fadfd29b8aa9bcc53efc5a7f",
	},
}

func TestTSPassKey(t *testing.T) {
	for _, test := range dktests {
		dk, err := NewTSPassKey(test.passphrase, []byte(test.salt))
		if err != nil {
			t.Errorf("%s: got unexpected error: %s", test.name, err)
			continue
		}
		if hex.EncodeToString(dk.PWHash()) != test.pwh {
			t.Errorf("%s: pwh = %x, expected %q", test.name, dk.PWHash(), test.pwh)
		}
		if hex.EncodeToString(dk.EdDSASeed()) != test.ekey {
			t.Errorf("%s: eddsa = %x, expected %q", test.name, dk.EdDSASeed(), test.ekey)
		}
		if hex.EncodeToString(dk.DHSeed()) != test.dkey {
			t.Errorf("%s: dh = %x, expected %q", test.name, dk.DHSeed(), test.dkey)
		}
		if hex.EncodeToString(dk.LksClientHalf()) != test.lkey {
			t.Errorf("%s: lks = %x, expected %q", test.name, dk.LksClientHalf(), test.lkey)
		}
	}
}
<commit_msg>fix regression in tests that resulted from #210<commit_after>package libkb

import (
	"encoding/hex"
	"testing"
)

type dktest struct {
	name       string
	passphrase string
	salt       string
	pwh        string
	ekey       string
	dkey       string
	lkey       string
}

var dktests = []dktest{
	{
		name:       "simple 1",
		passphrase: "my passphrase",
		salt:       "random salt zxcv",
		pwh:        "eb92a4bc72cbce98b80c5bfb391b353c37af5f3398e52c7cb436b73de97abc48",
		ekey:       "248f73a3645486c7d2c327da2bb7bb3129cf2347494d54ca9e35083f325ab57e",
		dkey:       "b4f8ffced85c240c4833afac527ce1f9be37e2e645fc020d41c31d8179ff7ef2",
		lkey:       "daf502dcc05af15e8c9b4c36a195566e1cfdd5dbb7593e268e0053e89ed74c0f",
	},
	{
		name:       "simple 2",
		passphrase: "my passphrase",
		salt:       "random salt qwer",
		pwh:        "5c44c619c7f29bc446af06ecd2d5c8d0a58db04970891aa18084fac8014c717a",
		ekey:       "c93b7470701a0623a062b849f0527b68faa568549926b77320e9030b12d29197",
		dkey:       "77375b7d2e59b9fdd4733dbead5ba66116cec508e05919b98332b97e437a75b0",
		lkey:       "5fec13005cca4f12ce0b624db02488d9ea4e5ef54b67649620c810e6b4e01376",
	},
	{
		name:       "simple 3",
		passphrase: "my passphrase is longer",
		salt:       "random salt zxcv",
		pwh:        "de526ef302f50283d0a0aecdc303e1f42c3b206060657bf03f781f076eec1459",
		ekey:       "199c9e10e5c9505c431cd2d3235873e7e113918511178afc341d28d48f1b5dd7",
		dkey:       "1c2c2d040fc743f939b1af6e90dde1a0e9181e2d6a0e584c75f7d363100493d5",
		lkey:       "66647f005c89d55efbc4683b2a86210d545d77e6fadfd29b8aa9bcc53efc5a7f",
	},
}

func TestTSPassKey(t *testing.T) {
	for _, test := range dktests {
		_, dk, err := StretchPassphrase(test.passphrase, []byte(test.salt))
		if err != nil {
			t.Errorf("%s: got unexpected error: %s", test.name, err)
			continue
		}
		if hex.EncodeToString(dk.PWHash()) != test.pwh {
			t.Errorf("%s: pwh = %x, expected %q", test.name, dk.PWHash(), test.pwh)
		}
		if hex.EncodeToString(dk.EdDSASeed()) != test.ekey {
			t.Errorf("%s: eddsa = %x, expected %q", test.name, dk.EdDSASeed(), test.ekey)
		}
		if hex.EncodeToString(dk.DHSeed()) != test.dkey {
			t.Errorf("%s: dh = %x, expected %q", test.name, dk.DHSeed(), test.dkey)
		}
		if hex.EncodeToString(dk.LksClientHalf()) != test.lkey {
			t.Errorf("%s: lks = %x, expected %q", test.name, dk.LksClientHalf(), test.lkey)
		}
	}
}
<|endoftext|>


<commit_before>package sync2

import (
	"fmt"
	"math/rand"
	"runtime"
	"sync"
	"testing"
	"time"

	"github.com/cosiner/gohper/testing2"
)

func TestSpinLock(t *testing.T) {
	tt := testing2.Wrap(t)
	if runtime.NumCPU() == 1 {
		return
	}

	runtime.GOMAXPROCS(2)

	var lock Spinlock
	go func() {
		lock.Lock()
		lock.Unlock()
	}()
	lock.Lock()
	time.Sleep(1 * time.Millisecond)
	lock.Unlock()

	defer tt.Recover()
	lock.Unlock()
}

func TestAutorefMutex(t *testing.T) {
	t.Log("start testing.")
	mu := NewAutorefMutex(false)

	keys := []string{"1", "2", "3"}
	keyNum := len(keys)
	wg := sync.WaitGroup{}

	routine := 21
	wg.Add(routine)

	for i := 0; i < routine; i++ {
		n := i + 1
		go func() {
			time.Sleep(time.Duration(n%3)*time.Millisecond)
			key := keys[rand.Intn(keyNum)]
			mu.Lock(key)
			t.Logf("routine %d locked %s", n, key)
			time.Sleep(time.Duration(n%3)*time.Millisecond)
			mu.Unlock(key)
			t.Logf("routine %d unlocked %s", n, key)

			wg.Done()
		}()
	}
	wg.Wait()
}

func BenchmarkAutorefMutex(b *testing.B) {
	mu := NewAutorefMutex(false)

	for i := 0; i < b.N; i++ {
		mu.Lock("a")
		mu.Unlock("a")
	}
}
<commit_msg>fixed testing<commit_after>package sync2

import (
	"math/rand"
	"runtime"
	"sync"
	"testing"
	"time"

	"github.com/cosiner/gohper/testing2"
)

func TestSpinLock(t *testing.T) {
	tt := testing2.Wrap(t)
	if runtime.NumCPU() == 1 {
		return
	}

	runtime.GOMAXPROCS(2)

	var lock Spinlock
	go func() {
		lock.Lock()
		lock.Unlock()
	}()
	lock.Lock()
	time.Sleep(1 * time.Millisecond)
	lock.Unlock()

	defer tt.Recover()
	lock.Unlock()
}

func TestAutorefMutex(t *testing.T) {
	t.Log("start testing.")
	mu := NewAutorefMutex(false)

	keys := []string{"1", "2", "3"}
	keyNum := len(keys)
	wg := sync.WaitGroup{}

	routine := 21
	wg.Add(routine)

	for i := 0; i < routine; i++ {
		n := i + 1
		go func() {
			time.Sleep(time.Duration(n%3) * time.Millisecond)
			key := keys[rand.Intn(keyNum)]
			mu.Lock(key)
			t.Logf("routine %d locked %s", n, key)
			time.Sleep(time.Duration(n%3) * time.Millisecond)
			mu.Unlock(key)
			t.Logf("routine %d unlocked %s", n, key)

			wg.Done()
		}()
	}
	wg.Wait()
}

func BenchmarkAutorefMutex(b *testing.B) {
	mu := NewAutorefMutex(false)

	for i := 0; i < b.N; i++ {
		mu.Lock("a")
		mu.Unlock("a")
	}
}
<|endoftext|>


<commit_before>package rxgo

import (
	"bufio"
	"context"
	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"strconv"
	"strings"
	"testing"
)

const signalCh = byte(0)

var mockError = errors.New("")

type mockIterable struct {
	iterator Iterator
}

type mockIterator struct {
	mock.Mock
}

type task struct {
	observable int
	item       int
	error      error
	close      bool
}

func (s *mockIterable) Iterator(ctx context.Context) Iterator {
	return s.iterator
}

func newMockObservable(iterator Iterator) Observable {
	return &observable{
		observableType: cold,
		iterable: &mockIterable{
			iterator: iterator,
		},
	}
}

func countTab(line string) int {
	i := 0
	for _, runeValue := range line {
		if runeValue == '\t' {
			i++
		} else {
			break
		}
	}
	return i
}

// TODO Causality with more than two observables
func mockObservables(t *testing.T, in string) []Observable {
	scanner := bufio.NewScanner(strings.NewReader(in))
	m := make(map[int]int)
	tasks := make([]task, 0)
	count := 0
	for scanner.Scan() {
		s := scanner.Text()
		if s == "" {
			continue
		}
		observable := countTab(s)
		v := strings.TrimSpace(s)
		switch v {
		case "x":
			tasks = append(tasks, task{
				observable: observable,
				close:      true,
			})
		case "e":
			tasks = append(tasks, task{
				observable: observable,
				error:      mockError,
			})
		default:
			n, err := strconv.Atoi(v)
			if err != nil {
				assert.FailNow(t, err.Error())
			}
			tasks = append(tasks, task{
				observable: observable,
				item:       n,
			})
		}
		if _, contains := m[observable]; !contains {
			m[observable] = count
			count++
		}
	}

	iterators := make([]*mockIterator, 0, len(m))
	calls := make([]*mock.Call, len(m))
	for i := 0; i < len(m); i++ {
		iterators = append(iterators, new(mockIterator))
	}

	item, err := args(tasks[0])
	call := iterators[0].On("Next", mock.Anything).Once().Return(item, err)
	calls[0] = call

	var lastCh chan struct{}
	lastObservableType := tasks[0].observable
	for i := 1; i < len(tasks); i++ {
		t := tasks[i]
		index := m[t.observable]
		obs := iterators[index]
		item, err := args(t)
		if lastObservableType == t.observable {
			if calls[index] == nil {
				calls[index] = obs.On("Next", mock.Anything).Once().Return(item, err)
			} else {
				calls[index].On("Next", mock.Anything).Once().Return(item, err)
			}
		} else {
			lastObservableType = t.observable
			if lastCh == nil {
				ch := make(chan struct{})
				lastCh = ch
				if calls[index] == nil {
					calls[index] = obs.On("Next", mock.Anything).Once().Return(item, err).
						Run(func(args mock.Arguments) {
							run(args, ch, nil)
						})
				} else {
					calls[index].On("Next", mock.Anything).Once().Return(item, err).
						Run(func(args mock.Arguments) {
							run(args, ch, nil)
						})
				}
			} else {
				var ch chan struct{}
				// If this is the latest task we do not set any wait channel
				if i != len(tasks)-1 {
					ch = make(chan struct{})
				}
				previous := lastCh
				if calls[index] == nil {
					calls[index] = obs.On("Next", mock.Anything).Once().Return(item, err).
						Run(func(args mock.Arguments) {
							run(args, ch, previous)
						})
				} else {
					calls[index].On("Next", mock.Anything).Once().Return(item, err).
						Run(func(args mock.Arguments) {
							run(args, ch, previous)
						})
				}
				lastCh = ch
			}
		}
	}

	observables := make([]Observable, 0, len(iterators))
	for _, iterator := range iterators {
		observables = append(observables, newMockObservable(iterator))
	}
	return observables
}

func args(t task) (interface{}, error) {
	if t.close {
		return nil, &NoSuchElementError{}
	}
	if t.error != nil {
		return t.error, nil
	}
	return t.item, nil
}

func run(args mock.Arguments, wait chan struct{}, send chan struct{}) {
	if send != nil {
		send <- struct{}{}
	}
	if wait == nil {
		return
	}
	if len(args) == 1 {
		if ctx, ok := args[0].(context.Context); ok {
			if sig, ok := ctx.Value(signalCh).(chan struct{}); ok {
				select {
				case <-wait:
				case <-ctx.Done():
					sig <- struct{}{}
				}
				return
			}
		}
	}
	<-wait
}

func (m *mockIterator) Next(ctx context.Context) (interface{}, error) {
	sig := make(chan struct{}, 1)
	defer close(sig)
	outputs := m.Called(context.WithValue(ctx, signalCh, sig))
	select {
	case <-sig:
		return nil, &CancelledIteratorError{}
	default:
		return outputs.Get(0), outputs.Error(1)
	}
}
<commit_msg>gofmt<commit_after>package rxgo

import (
	"bufio"
	"context"
	"strconv"
	"strings"
	"testing"

	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

const signalCh = byte(0)

var mockError = errors.New("")

type mockIterable struct {
	iterator Iterator
}

type mockIterator struct {
	mock.Mock
}

type task struct {
	observable int
	item       int
	error      error
	close      bool
}

func (s *mockIterable) Iterator(ctx context.Context) Iterator {
	return s.iterator
}

func newMockObservable(iterator Iterator) Observable {
	return &observable{
		observableType: cold,
		iterable: &mockIterable{
			iterator: iterator,
		},
	}
}

func countTab(line string) int {
	i := 0
	for _, runeValue := range line {
		if runeValue == '\t' {
			i++
		} else {
			break
		}
	}
	return i
}

// TODO Causality with more than two observables
func mockObservables(t *testing.T, in string) []Observable {
	scanner := bufio.NewScanner(strings.NewReader(in))
	m := make(map[int]int)
	tasks := make([]task, 0)
	count := 0
	for scanner.Scan() {
		s := scanner.Text()
		if s == "" {
			continue
		}
		observable := countTab(s)
		v := strings.TrimSpace(s)
		switch v {
		case "x":
			tasks = append(tasks, task{
				observable: observable,
				close:      true,
			})
		case "e":
			tasks = append(tasks, task{
				observable: observable,
				error:      mockError,
			})
		default:
			n, err := strconv.Atoi(v)
			if err != nil {
				assert.FailNow(t, err.Error())
			}
			tasks = append(tasks, task{
				observable: observable,
				item:       n,
			})
		}
		if _, contains := m[observable]; !contains {
			m[observable] = count
			count++
		}
	}

	iterators := make([]*mockIterator, 0, len(m))
	calls := make([]*mock.Call, len(m))
	for i := 0; i < len(m); i++ {
		iterators = append(iterators, new(mockIterator))
	}

	item, err := args(tasks[0])
	call := iterators[0].On("Next", mock.Anything).Once().Return(item, err)
	calls[0] = call

	var lastCh chan struct{}
	lastObservableType := tasks[0].observable
	for i := 1; i < len(tasks); i++ {
		t := tasks[i]
		index := m[t.observable]
		obs := iterators[index]
		item, err := args(t)
		if lastObservableType == t.observable {
			if calls[index] == nil {
				calls[index] = obs.On("Next", mock.Anything).Once().Return(item, err)
			} else {
				calls[index].On("Next", mock.Anything).Once().Return(item, err)
			}
		} else {
			lastObservableType = t.observable
			if lastCh == nil {
				ch := make(chan struct{})
				lastCh = ch
				if calls[index] == nil {
					calls[index] = obs.On("Next", mock.Anything).Once().Return(item, err).
						Run(func(args mock.Arguments) {
							run(args, ch, nil)
						})
				} else {
					calls[index].On("Next", mock.Anything).Once().Return(item, err).
						Run(func(args mock.Arguments) {
							run(args, ch, nil)
						})
				}
			} else {
				var ch chan struct{}
				// If this is the latest task we do not set any wait channel
				if i != len(tasks)-1 {
					ch = make(chan struct{})
				}
				previous := lastCh
				if calls[index] == nil {
					calls[index] = obs.On("Next", mock.Anything).Once().Return(item, err).
						Run(func(args mock.Arguments) {
							run(args, ch, previous)
						})
				} else {
					calls[index].On("Next", mock.Anything).Once().Return(item, err).
						Run(func(args mock.Arguments) {
							run(args, ch, previous)
						})
				}
				lastCh = ch
			}
		}
	}

	observables := make([]Observable, 0, len(iterators))
	for _, iterator := range iterators {
		observables = append(observables, newMockObservable(iterator))
	}
	return observables
}

func args(t task) (interface{}, error) {
	if t.close {
		return nil, &NoSuchElementError{}
	}
	if t.error != nil {
		return t.error, nil
	}
	return t.item, nil
}

func run(args mock.Arguments, wait chan struct{}, send chan struct{}) {
	if send != nil {
		send <- struct{}{}
	}
	if wait == nil {
		return
	}
	if len(args) == 1 {
		if ctx, ok := args[0].(context.Context); ok {
			if sig, ok := ctx.Value(signalCh).(chan struct{}); ok {
				select {
				case <-wait:
				case <-ctx.Done():
					sig <- struct{}{}
				}
				return
			}
		}
	}
	<-wait
}

func (m *mockIterator) Next(ctx context.Context) (interface{}, error) {
	sig := make(chan struct{}, 1)
	defer close(sig)
	outputs := m.Called(context.WithValue(ctx, signalCh, sig))
	select {
	case <-sig:
		return nil, &CancelledIteratorError{}
	default:
		return outputs.Get(0), outputs.Error(1)
	}
}
<|endoftext|>


<commit_before>package notification

import (
	"encoding/json"
	"fmt"
	"koding/db/mongodb/modelhelper"
	socialapimodels "socialapi/models"
	"socialapi/workers/notification/models"
	"time"

	"github.com/koding/logging"
	"github.com/koding/rabbitmq"
	"github.com/koding/worker"
	"github.com/streadway/amqp"
)

type Action func(*NotificationWorkerController, []byte) error

type NotificationWorkerController struct {
	routes          map[string]Action
	log             logging.Logger
	rmqConn         *amqp.Connection
	notifierRmqConn *amqp.Connection
	cacheEnabled    bool
}

func (n *NotificationWorkerController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	n.log.Error("an error occured: %s", err)
	delivery.Ack(false)

	return false
}

func NewNotificationWorkerController(rmq *rabbitmq.RabbitMQ, log logging.Logger, cacheEnabled bool) (*NotificationWorkerController, error) {
	rmqConn, err := rmq.Connect("NewNotificationWorkerController")
	if err != nil {
		return nil, err
	}

	nwc := &NotificationWorkerController{
		log:          log,
		rmqConn:      rmqConn.Conn(),
		cacheEnabled: cacheEnabled,
	}

	routes := map[string]Action{
		"api.message_reply_created":       (*NotificationWorkerController).CreateReplyNotification,
		"api.interaction_created":         (*NotificationWorkerController).CreateInteractionNotification,
		"api.channel_participant_created": (*NotificationWorkerController).JoinGroup,
		"api.channel_participant_updated": (*NotificationWorkerController).LeaveGroup,
	}

	nwc.routes = routes

	return nwc, nil
}

// copy/paste
func (n *NotificationWorkerController) HandleEvent(event string, data []byte) error {
	n.log.Debug("New Event Received %s", event)
	handler, ok := n.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(n, data)
}

func (n *NotificationWorkerController) CreateReplyNotification(data []byte) error {
	mr, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	// fetch replier
	reply := socialapimodels.NewChannelMessage()
	if err := reply.ById(mr.ReplyId); err != nil {
		return err
	}

	rn := models.NewReplyNotification()
	rn.TargetId = mr.MessageId
	rn.NotifierId = reply.AccountId
	subscribedAt := time.Now()

	nc, err := models.CreateNotificationContent(rn)
	if err != nil {
		return err
	}

	cm := socialapimodels.NewChannelMessage()
	// notify message owner
	if err = cm.ById(mr.MessageId); err != nil {
		return err
	}

	// if it is not notifier's own message then add owner to subscribers
	if cm.AccountId != rn.NotifierId {
		n.subscribe(nc.Id, cm.AccountId, subscribedAt)
	}

	notifiedUsers, err := rn.GetNotifiedUsers(nc.Id)
	if err != nil {
		return err
	}

	mentionedUsers, err := n.CreateMentionNotification(reply)
	if err != nil {
		return err
	}

	notifiedUsers = filterRepliers(notifiedUsers, mentionedUsers)

	notifierSubscribed := false
	for _, recipient := range notifiedUsers {
		if recipient == rn.NotifierId {
			notifierSubscribed = true
		}
		n.notify(nc.Id, recipient, subscribedAt)
	}

	// if not subcribed, subscribe the actor to message
	if !notifierSubscribed {
		n.subscribe(nc.Id, rn.NotifierId, subscribedAt)
	}

	return nil
}

func (n *NotificationWorkerController) CreateMentionNotification(reply *socialapimodels.ChannelMessage) ([]int64, error) {
	mentionedUserIds := make([]int64, 0)
	usernames := reply.GetMentionedUsernames()

	// message does not contain any mentioned users
	if len(usernames) == 0 {
		return mentionedUserIds, nil
	}

	mentionedUserIds, err := fetchParticipantIds(usernames)
	if err != nil {
		return nil, err
	}

	for _, mentionedUser := range mentionedUserIds {
		if mentionedUser == reply.AccountId {
			continue
		}
		mn := models.NewMentionNotification()
		mn.TargetId = reply.Id
		mn.NotifierId = reply.AccountId
		nc, err := models.CreateNotificationContent(mn)
		if err != nil {
			return nil, err
		}

		notification := models.NewNotification()
		notification.NotificationContentId = nc.Id
		notification.AccountId = mentionedUser
		notification.ActivatedAt = time.Now() // enables notification immediately
		if err = notification.Upsert(); err != nil {
			n.log.Error("An error occurred while notifying user %d: %s", reply.AccountId, err.Error())
		}
	}

	return mentionedUserIds, nil
}

func (n *NotificationWorkerController) notify(contentId, notifierId int64, subscribedAt time.Time) {
	notification := buildNotification(contentId, notifierId, subscribedAt)
	if err := notification.Upsert(); err != nil {
		n.log.Error("An error occurred while notifying user %d: %s", notification.AccountId, err.Error())
	}
}

func (n *NotificationWorkerController) subscribe(contentId, notifierId int64, subscribedAt time.Time) {
	notification := buildNotification(contentId, notifierId, subscribedAt)
	notification.SubscribeOnly = true
	if err := notification.Create(); err != nil {
		n.log.Error("An error occurred while subscribing user %d: %s", notification.AccountId, err.Error())
	}
}

func buildNotification(contentId, notifierId int64, subscribedAt time.Time) *models.Notification {
	notification := models.NewNotification()
	notification.NotificationContentId = contentId
	notification.AccountId = notifierId
	notification.SubscribedAt = subscribedAt

	return notification
}

func (n *NotificationWorkerController) CreateInteractionNotification(data []byte) error {
	i, err := mapMessageToInteraction(data)
	if err != nil {
		return err
	}

	cm := socialapimodels.NewChannelMessage()
	if err := cm.ById(i.MessageId); err != nil {
		return err
	}

	// user likes her own message, so we bypass notification
	if cm.AccountId == i.AccountId {
		return nil
	}

	in := models.NewInteractionNotification(i.TypeConstant)
	in.TargetId = i.MessageId
	in.NotifierId = i.AccountId
	nc, err := models.CreateNotificationContent(in)
	if err != nil {
		return err
	}

	notification := models.NewNotification()
	notification.NotificationContentId = nc.Id
	notification.AccountId = cm.AccountId // notify message owner
	notification.ActivatedAt = time.Now() // enables notification immediately
	if err = notification.Upsert(); err != nil {
		return fmt.Errorf("An error occurred while notifying user %d: %s", cm.AccountId, err.Error())
	}

	return nil
}

func (n *NotificationWorkerController) JoinGroup(data []byte) error {
	cp, err := mapMessageToChannelParticipant(data)
	if err != nil {
		return err
	}

	return processChannelParticipant(cp, models.NotificationContent_TYPE_JOIN)
}

func (n *NotificationWorkerController) LeaveGroup(data []byte) error {
	cp, err := mapMessageToChannelParticipant(data)
	if err != nil {
		return err
	}

	if cp.StatusConstant == socialapimodels.ChannelParticipant_STATUS_LEFT {
		return processChannelParticipant(cp, models.NotificationContent_TYPE_LEAVE)
	}

	return nil
}

func processChannelParticipant(cp *socialapimodels.ChannelParticipant, typeConstant string) error {
	c := socialapimodels.NewChannel()
	if err := c.ById(cp.ChannelId); err != nil {
		return err
	}

	switch c.TypeConstant {
	case socialapimodels.Channel_TYPE_GROUP:
		return interactGroup(cp, c, typeConstant)
	case socialapimodels.Channel_TYPE_FOLLOWERS:
		return interactFollow(cp, c)
	}

	return nil
}

func interactFollow(cp *socialapimodels.ChannelParticipant, c *socialapimodels.Channel) error {
	// TODO refactor this part
	nI := models.NewFollowNotification()
	nI.TargetId = cp.ChannelId
	nI.NotifierId = cp.AccountId
	nc, err := models.CreateNotificationContent(nI)
	if err != nil {
		return err
	}

	notification := models.NewNotification()
	notification.NotificationContentId = nc.Id
	notification.AccountId = c.CreatorId  // notify channel owner
	notification.ActivatedAt = time.Now() // enables notification immediately
	if err = notification.Upsert(); err != nil {
		return err
	}

	return nil
}

func interactGroup(cp *socialapimodels.ChannelParticipant, c *socialapimodels.Channel, typeConstant string) error {

	// user joins her own group, so we bypass notification
	if c.CreatorId == cp.AccountId {
		return nil
	}

	nI := models.NewGroupNotification(typeConstant)
	nI.TargetId = cp.ChannelId
	nI.NotifierId = cp.AccountId
	nc, err := models.CreateNotificationContent(nI)
	if err != nil {
		return err
	}

	//TODO all group admins (if there exists) should be notified
	notification := models.NewNotification()
	notification.NotificationContentId = nc.Id
	notification.AccountId = c.CreatorId  // notify channel owner
	notification.ActivatedAt = time.Now() // enables notification immediately
	if err = notification.Upsert(); err != nil {
		return err
	}

	return nil
}

func mapMessageToChannelParticipant(data []byte) (*socialapimodels.ChannelParticipant, error) {
	cp := socialapimodels.NewChannelParticipant()
	if err := json.Unmarshal(data, cp); err != nil {
		return nil, err
	}

	return cp, nil
}

func mapMessageToMessageReply(data []byte) (*socialapimodels.MessageReply, error) {
	mr := socialapimodels.NewMessageReply()
	if err := json.Unmarshal(data, mr); err != nil {
		return nil, err
	}

	return mr, nil
}

func mapMessageToInteraction(data []byte) (*socialapimodels.Interaction, error) {
	i := socialapimodels.NewInteraction()
	if err := json.Unmarshal(data, i); err != nil {
		return nil, err
	}

	return i, nil
}

// copy/paste
func fetchParticipantIds(participantNames []string) ([]int64, error) {
	participantIds := make([]int64, len(participantNames))
	for i, participantName := range participantNames {
		account, err := modelhelper.GetAccount(participantName)
		if err != nil {
			return nil, err
		}
		a := socialapimodels.NewAccount()
		a.Id = account.SocialApiId
		a.OldId = account.Id.Hex()
		// fetch or create social api id
		if a.Id == 0 {
			if err := a.FetchOrCreate(); err != nil {
				return nil, err
			}
		}
		participantIds[i] = a.Id
	}

	return participantIds, nil
}

func filterRepliers(repliers, mentionedUsers []int64) []int64 {
	mentionMap := map[int64]struct{}{}
	flattened := make([]int64, 0)
	if len(mentionedUsers) == 0 {
		return repliers
	}

	for _, user := range mentionedUsers {
		mentionMap[user] = struct{}{}
	}

	for _, replier := range repliers {
		if _, ok := mentionMap[replier]; !ok {
			flattened = append(flattened, replier)
		}
	}

	return flattened
}
<commit_msg>Social: fixed follow notification received when a user is unfollowed bug<commit_after>package notification

import (
	"encoding/json"
	"fmt"
	"koding/db/mongodb/modelhelper"
	socialapimodels "socialapi/models"
	"socialapi/workers/notification/models"
	"time"

	"github.com/koding/logging"
	"github.com/koding/rabbitmq"
	"github.com/koding/worker"
	"github.com/streadway/amqp"
)

type Action func(*NotificationWorkerController, []byte) error

type NotificationWorkerController struct {
	routes          map[string]Action
	log             logging.Logger
	rmqConn         *amqp.Connection
	notifierRmqConn *amqp.Connection
	cacheEnabled    bool
}

func (n *NotificationWorkerController) DefaultErrHandler(delivery amqp.Delivery, err error) bool {
	n.log.Error("an error occured: %s", err)
	delivery.Ack(false)

	return false
}

func NewNotificationWorkerController(rmq *rabbitmq.RabbitMQ, log logging.Logger, cacheEnabled bool) (*NotificationWorkerController, error) {
	rmqConn, err := rmq.Connect("NewNotificationWorkerController")
	if err != nil {
		return nil, err
	}

	nwc := &NotificationWorkerController{
		log:          log,
		rmqConn:      rmqConn.Conn(),
		cacheEnabled: cacheEnabled,
	}

	routes := map[string]Action{
		"api.message_reply_created":       (*NotificationWorkerController).CreateReplyNotification,
		"api.interaction_created":         (*NotificationWorkerController).CreateInteractionNotification,
		"api.channel_participant_created": (*NotificationWorkerController).JoinChannel,
		"api.channel_participant_updated": (*NotificationWorkerController).LeaveChannel,
	}

	nwc.routes = routes

	return nwc, nil
}

// copy/paste
func (n *NotificationWorkerController) HandleEvent(event string, data []byte) error {
	n.log.Debug("New Event Received %s", event)
	handler, ok := n.routes[event]
	if !ok {
		return worker.HandlerNotFoundErr
	}

	return handler(n, data)
}

func (n *NotificationWorkerController) CreateReplyNotification(data []byte) error {
	mr, err := mapMessageToMessageReply(data)
	if err != nil {
		return err
	}

	// fetch replier
	reply := socialapimodels.NewChannelMessage()
	if err := reply.ById(mr.ReplyId); err != nil {
		return err
	}

	rn := models.NewReplyNotification()
	rn.TargetId = mr.MessageId
	rn.NotifierId = reply.AccountId
	subscribedAt := time.Now()

	nc, err := models.CreateNotificationContent(rn)
	if err != nil {
		return err
	}

	cm := socialapimodels.NewChannelMessage()
	// notify message owner
	if err = cm.ById(mr.MessageId); err != nil {
		return err
	}

	// if it is not notifier's own message then add owner to subscribers
	if cm.AccountId != rn.NotifierId {
		n.subscribe(nc.Id, cm.AccountId, subscribedAt)
	}

	notifiedUsers, err := rn.GetNotifiedUsers(nc.Id)
	if err != nil {
		return err
	}

	mentionedUsers, err := n.CreateMentionNotification(reply)
	if err != nil {
		return err
	}

	notifiedUsers = filterRepliers(notifiedUsers, mentionedUsers)

	notifierSubscribed := false
	for _, recipient := range notifiedUsers {
		if recipient == rn.NotifierId {
			notifierSubscribed = true
		}
		n.notify(nc.Id, recipient, subscribedAt)
	}

	// if not subcribed, subscribe the actor to message
	if !notifierSubscribed {
		n.subscribe(nc.Id, rn.NotifierId, subscribedAt)
	}

	return nil
}

func (n *NotificationWorkerController) CreateMentionNotification(reply *socialapimodels.ChannelMessage) ([]int64, error) {
	mentionedUserIds := make([]int64, 0)
	usernames := reply.GetMentionedUsernames()

	// message does not contain any mentioned users
	if len(usernames) == 0 {
		return mentionedUserIds, nil
	}

	mentionedUserIds, err := fetchParticipantIds(usernames)
	if err != nil {
		return nil, err
	}

	for _, mentionedUser := range mentionedUserIds {
		if mentionedUser == reply.AccountId {
			continue
		}
		mn := models.NewMentionNotification()
		mn.TargetId = reply.Id
		mn.NotifierId = reply.AccountId
		nc, err := models.CreateNotificationContent(mn)
		if err != nil {
			return nil, err
		}

		notification := models.NewNotification()
		notification.NotificationContentId = nc.Id
		notification.AccountId = mentionedUser
		notification.ActivatedAt = time.Now() // enables notification immediately
		if err = notification.Upsert(); err != nil {
			n.log.Error("An error occurred while notifying user %d: %s", reply.AccountId, err.Error())
		}
	}

	return mentionedUserIds, nil
}

func (n *NotificationWorkerController) notify(contentId, notifierId int64, subscribedAt time.Time) {
	notification := buildNotification(contentId, notifierId, subscribedAt)
	if err := notification.Upsert(); err != nil {
		n.log.Error("An error occurred while notifying user %d: %s", notification.AccountId, err.Error())
	}
}

func (n *NotificationWorkerController) subscribe(contentId, notifierId int64, subscribedAt time.Time) {
	notification := buildNotification(contentId, notifierId, subscribedAt)
	notification.SubscribeOnly = true
	if err := notification.Create(); err != nil {
		n.log.Error("An error occurred while subscribing user %d: %s", notification.AccountId, err.Error())
	}
}

func buildNotification(contentId, notifierId int64, subscribedAt time.Time) *models.Notification {
	notification := models.NewNotification()
	notification.NotificationContentId = contentId
	notification.AccountId = notifierId
	notification.SubscribedAt = subscribedAt

	return notification
}

func (n *NotificationWorkerController) CreateInteractionNotification(data []byte) error {
	i, err := mapMessageToInteraction(data)
	if err != nil {
		return err
	}

	cm := socialapimodels.NewChannelMessage()
	if err := cm.ById(i.MessageId); err != nil {
		return err
	}

	// user likes her own message, so we bypass notification
	if cm.AccountId == i.AccountId {
		return nil
	}

	in := models.NewInteractionNotification(i.TypeConstant)
	in.TargetId = i.MessageId
	in.NotifierId = i.AccountId
	nc, err := models.CreateNotificationContent(in)
	if err != nil {
		return err
	}

	notification := models.NewNotification()
	notification.NotificationContentId = nc.Id
	notification.AccountId = cm.AccountId // notify message owner
	notification.ActivatedAt = time.Now() // enables notification immediately
	if err = notification.Upsert(); err != nil {
		return fmt.Errorf("An error occurred while notifying user %d: %s", cm.AccountId, err.Error())
	}

	return nil
}

func (n *NotificationWorkerController) JoinChannel(data []byte) error {
	cp, err := mapMessageToChannelParticipant(data)
	if err != nil {
		return err
	}

	return processChannelParticipant(cp, models.NotificationContent_TYPE_JOIN)
}

func (n *NotificationWorkerController) LeaveChannel(data []byte) error {
	cp, err := mapMessageToChannelParticipant(data)
	if err != nil {
		return err
	}

	if cp.StatusConstant == socialapimodels.ChannelParticipant_STATUS_LEFT {
		return processChannelParticipant(cp, models.NotificationContent_TYPE_LEAVE)
	}

	return nil
}

func processChannelParticipant(cp *socialapimodels.ChannelParticipant, typeConstant string) error {
	c := socialapimodels.NewChannel()
	if err := c.ById(cp.ChannelId); err != nil {
		return err
	}

	switch c.TypeConstant {
	case socialapimodels.Channel_TYPE_GROUP:
		return interactGroup(cp, c, typeConstant)
	case socialapimodels.Channel_TYPE_FOLLOWERS:
		return interactFollow(cp, c)
	}

	return nil
}

func interactFollow(cp *socialapimodels.ChannelParticipant, c *socialapimodels.Channel) error {
	if cp.StatusConstant == socialapimodels.ChannelParticipant_STATUS_LEFT {
		return nil
	}
	nI := models.NewFollowNotification()
	nI.TargetId = cp.ChannelId
	nI.NotifierId = cp.AccountId
	nc, err := models.CreateNotificationContent(nI)
	if err != nil {
		return err
	}

	notification := models.NewNotification()
	notification.NotificationContentId = nc.Id
	notification.AccountId = c.CreatorId  // notify channel owner
	notification.ActivatedAt = time.Now() // enables notification immediately
	if err = notification.Upsert(); err != nil {
		return err
	}

	return nil
}

func interactGroup(cp *socialapimodels.ChannelParticipant, c *socialapimodels.Channel, typeConstant string) error {

	// user joins her own group, so we bypass notification
	if c.CreatorId == cp.AccountId {
		return nil
	}

	nI := models.NewGroupNotification(typeConstant)
	nI.TargetId = cp.ChannelId
	nI.NotifierId = cp.AccountId
	nc, err := models.CreateNotificationContent(nI)
	if err != nil {
		return err
	}

	//TODO all group admins (if there exists) should be notified
	notification := models.NewNotification()
	notification.NotificationContentId = nc.Id
	notification.AccountId = c.CreatorId  // notify channel owner
	notification.ActivatedAt = time.Now() // enables notification immediately
	if err = notification.Upsert(); err != nil {
		return err
	}

	return nil
}

func mapMessageToChannelParticipant(data []byte) (*socialapimodels.ChannelParticipant, error) {
	cp := socialapimodels.NewChannelParticipant()
	if err := json.Unmarshal(data, cp); err != nil {
		return nil, err
	}

	return cp, nil
}

func mapMessageToMessageReply(data []byte) (*socialapimodels.MessageReply, error) {
	mr := socialapimodels.NewMessageReply()
	if err := json.Unmarshal(data, mr); err != nil {
		return nil, err
	}

	return mr, nil
}

func mapMessageToInteraction(data []byte) (*socialapimodels.Interaction, error) {
	i := socialapimodels.NewInteraction()
	if err := json.Unmarshal(data, i); err != nil {
		return nil, err
	}

	return i, nil
}

// copy/paste
func fetchParticipantIds(participantNames []string) ([]int64, error) {
	participantIds := make([]int64, len(participantNames))
	for i, participantName := range participantNames {
		account, err := modelhelper.GetAccount(participantName)
		if err != nil {
			return nil, err
		}
		a := socialapimodels.NewAccount()
		a.Id = account.SocialApiId
		a.OldId = account.Id.Hex()
		// fetch or create social api id
		if a.Id == 0 {
			if err := a.FetchOrCreate(); err != nil {
				return nil, err
			}
		}
		participantIds[i] = a.Id
	}

	return participantIds, nil
}

func filterRepliers(repliers, mentionedUsers []int64) []int64 {
	mentionMap := map[int64]struct{}{}
	flattened := make([]int64, 0)
	if len(mentionedUsers) == 0 {
		return repliers
	}

	for _, user := range mentionedUsers {
		mentionMap[user] = struct{}{}
	}

	for _, replier := range repliers {
		if _, ok := mentionMap[replier]; !ok {
			flattened = append(flattened, replier)
		}
	}

	return flattened
}
<|endoftext|>


<commit_before>package guardiancmd

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"code.cloudfoundry.org/commandrunner"
	"code.cloudfoundry.org/commandrunner/linux_command_runner"
	"code.cloudfoundry.org/garden-shed/distclient"
	quotaed_aufs "code.cloudfoundry.org/garden-shed/docker_drivers/aufs"
	"code.cloudfoundry.org/garden-shed/layercake"
	"code.cloudfoundry.org/garden-shed/layercake/cleaner"
	"code.cloudfoundry.org/garden-shed/quota_manager"
	"code.cloudfoundry.org/garden-shed/repository_fetcher"
	"code.cloudfoundry.org/garden-shed/rootfs_provider"
	"code.cloudfoundry.org/guardian/gardener"
	"code.cloudfoundry.org/guardian/kawasaki"
	"code.cloudfoundry.org/guardian/kawasaki/dns"
	"code.cloudfoundry.org/guardian/logging"
	"code.cloudfoundry.org/guardian/rundmc"
	"code.cloudfoundry.org/guardian/rundmc/bundlerules"
	"code.cloudfoundry.org/guardian/rundmc/cgroups"
	"code.cloudfoundry.org/guardian/rundmc/execrunner/dadoo"
	"code.cloudfoundry.org/guardian/rundmc/preparerootfs"
	"code.cloudfoundry.org/guardian/rundmc/runrunc"
	"code.cloudfoundry.org/guardian/rundmc/signals"
	"code.cloudfoundry.org/idmapper"
	"code.cloudfoundry.org/lager"
	"github.com/docker/docker/daemon/graphdriver"
	"github.com/docker/docker/graph"
	"github.com/eapache/go-resiliency/retrier"
	specs "github.com/opencontainers/runtime-spec/specs-go"
)

type LinuxFactory struct {
	config           *ServerCommand
	commandRunner    commandrunner.CommandRunner
	signallerFactory *signals.SignallerFactory
	uidMappings      idmapper.MappingList
	gidMappings      idmapper.MappingList
}

func (cmd *ServerCommand) NewGardenFactory() GardenFactory {
	uidMappings, gidMappings := cmd.idMappings()
	return &LinuxFactory{
		config:           cmd,
		commandRunner:    linux_command_runner.New(),
		signallerFactory: &signals.SignallerFactory{PidGetter: wirePidfileReader()},
		uidMappings:      uidMappings,
		gidMappings:      gidMappings,
	}
}

func (f *LinuxFactory) CommandRunner() commandrunner.CommandRunner {
	return f.commandRunner
}

func (f *LinuxFactory) WireVolumizer(logger lager.Logger) gardener.Volumizer {
	if f.config.Image.Plugin.Path() != "" || f.config.Image.PrivilegedPlugin.Path() != "" {
		return f.config.wireImagePlugin(f.commandRunner, f.uidMappings.Map(0), f.gidMappings.Map(0))
	}

	if f.config.Graph.Dir == "" {
		return gardener.NoopVolumizer{}
	}

	shed := f.wireShed(logger)
	return gardener.NewVolumeProvider(shed, shed, gardener.CommandFactory(preparerootfs.Command), f.commandRunner, f.uidMappings.Map(0), f.gidMappings.Map(0))
}

func wireEnvFunc() runrunc.EnvFunc {
	return runrunc.EnvFunc(runrunc.UnixEnvFor)
}

func (f *LinuxFactory) WireMkdirer() runrunc.Mkdirer {
	if runningAsRoot() {
		return bundlerules.MkdirChowner{Command: preparerootfs.Command, CommandRunner: f.commandRunner}
	}

	return NoopMkdirer{}
}

type NoopMkdirer struct{}

func (NoopMkdirer) MkdirAs(rootFSPathFile string, uid, gid int, mode os.FileMode, recreate bool, path ...string) error {
	return nil
}

func (f *LinuxFactory) WireExecRunner(runMode string) runrunc.ExecRunner {
	return dadoo.NewExecRunner(
		f.config.Bin.Dadoo.Path(),
		f.config.Runtime.Plugin,
		f.signallerFactory,
		f.commandRunner,
		f.config.Containers.CleanupProcessDirsOnWait,
		runMode,
	)
}

func (f *LinuxFactory) WireCgroupsStarter(logger lager.Logger) gardener.Starter {
	return createCgroupsStarter(logger, f.config.Server.Tag, &cgroups.OSChowner{})
}

func (cmd *SetupCommand) WireCgroupsStarter(logger lager.Logger) gardener.Starter {
	return createCgroupsStarter(logger, cmd.Tag, &cgroups.OSChowner{UID: cmd.RootlessUID, GID: cmd.RootlessGID})
}

func createCgroupsStarter(logger lager.Logger, tag string, chowner cgroups.Chowner) gardener.Starter {
	cgroupsMountpoint := "/sys/fs/cgroup"
	gardenCgroup := "garden"
	if tag != "" {
		cgroupsMountpoint = filepath.Join(os.TempDir(), fmt.Sprintf("cgroups-%s", tag))
		gardenCgroup = fmt.Sprintf("%s-%s", gardenCgroup, tag)
	}

	return cgroups.NewStarter(logger, mustOpen("/proc/cgroups"), mustOpen("/proc/self/cgroup"),
		cgroupsMountpoint, gardenCgroup, allowedDevices, linux_command_runner.New(), chowner)
}

func (f *LinuxFactory) WireResolvConfigurer() kawasaki.DnsResolvConfigurer {
	return &kawasaki.ResolvConfigurer{
		HostsFileCompiler: &dns.HostsFileCompiler{},
		ResolvCompiler:    &dns.ResolvCompiler{},
		ResolvFilePath:    "/etc/resolv.conf",
		DepotDir:          f.config.Containers.Dir,
	}
}

func (f *LinuxFactory) WireRootfsFileCreator() rundmc.RootfsFileCreator {
	return preparerootfs.SymlinkRefusingFileCreator{}
}

func defaultBindMounts(binInitPath string) []specs.Mount {
	devptsGid := 0
	if runningAsRoot() {
		devptsGid = 5
	}

	return []specs.Mount{
		{Type: "sysfs", Source: "sysfs", Destination: "/sys", Options: []string{"nosuid", "noexec", "nodev", "ro"}},
		{Type: "tmpfs", Source: "tmpfs", Destination: "/dev/shm"},
		{Type: "devpts", Source: "devpts", Destination: "/dev/pts",
			Options: []string{"nosuid", "noexec", "newinstance", fmt.Sprintf("gid=%d", devptsGid), "ptmxmode=0666", "mode=0620"}},
		{Type: "bind", Source: binInitPath, Destination: "/tmp/garden-init", Options: []string{"bind"}},
	}
}

func privilegedMounts() []specs.Mount {
	return []specs.Mount{
		{Type: "proc", Source: "proc", Destination: "/proc", Options: []string{"nosuid", "noexec", "nodev"}},
	}
}

func unprivilegedMounts() []specs.Mount {
	return []specs.Mount{
		{Type: "proc", Source: "proc", Destination: "/proc", Options: []string{"nosuid", "noexec", "nodev"}},
		{Type: "cgroup", Source: "cgroup", Destination: "/sys/fs/cgroup", Options: []string{"ro", "nosuid", "noexec", "nodev"}},
	}
}

func getPrivilegedDevices() []specs.LinuxDevice {
	return []specs.LinuxDevice{fuseDevice}
}

func bindMountPoints() []string {
	return []string{"/etc/hosts", "/etc/resolv.conf"}
}

func mustGetMaxValidUID() int {
	return idmapper.MustGetMaxValidUID()
}

func ensureServerSocketDoesNotLeak(socketFD uintptr) error {
	_, _, errNo := syscall.Syscall(syscall.SYS_FCNTL, socketFD, syscall.F_SETFD, syscall.FD_CLOEXEC)
	if errNo != 0 {
		return fmt.Errorf("setting cloexec on server socket: %s", errNo)
	}
	return nil
}

func createCmd() string {
	return "run"
}

func createCmdExtraArgs() []string {
	return []string{"--detach"}
}

func (f *LinuxFactory) wireShed(logger lager.Logger) *rootfs_provider.CakeOrdinator {

	graphRoot := f.config.Graph.Dir
	logger = logger.Session(gardener.VolumizerSession, lager.Data{"graphRoot": graphRoot})
	runner := &logging.Runner{CommandRunner: linux_command_runner.New(), Logger: logger}

	if err := os.MkdirAll(graphRoot, 0755); err != nil {
		logger.Fatal("failed-to-create-graph-directory", err)
	}

	dockerGraphDriver, err := graphdriver.New(graphRoot, nil)
	if err != nil {
		logger.Fatal("failed-to-construct-graph-driver", err)
	}

	backingStoresPath := filepath.Join(graphRoot, "backing_stores")
	if mkdirErr := os.MkdirAll(backingStoresPath, 0660); mkdirErr != nil {
		logger.Fatal("failed-to-mkdir-backing-stores", mkdirErr)
	}

	quotaedGraphDriver := &quotaed_aufs.QuotaedDriver{
		GraphDriver: dockerGraphDriver,
		Unmount:     quotaed_aufs.Unmount,
		BackingStoreMgr: &quotaed_aufs.BackingStore{
			RootPath: backingStoresPath,
			Logger:   logger.Session("backing-store-mgr"),
		},
		LoopMounter: &quotaed_aufs.Loop{
			Retrier: retrier.New(retrier.ConstantBackoff(200, 500*time.Millisecond), nil),
			Logger:  logger.Session("loop-mounter"),
		},
		Retrier:  retrier.New(retrier.ConstantBackoff(200, 500*time.Millisecond), nil),
		RootPath: graphRoot,
		Logger:   logger.Session("quotaed-driver"),
	}

	dockerGraph, err := graph.NewGraph(graphRoot, quotaedGraphDriver)
	if err != nil {
		logger.Fatal("failed-to-construct-graph", err)
	}

	var cake layercake.Cake = &layercake.Docker{
		Graph:  dockerGraph,
		Driver: quotaedGraphDriver,
	}

	if cake.DriverName() == "aufs" {
		cake = &layercake.AufsCake{
			Cake:      cake,
			Runner:    runner,
			GraphRoot: graphRoot,
		}
	}

	repoFetcher := repository_fetcher.Retryable{
		RepositoryFetcher: &repository_fetcher.CompositeFetcher{
			LocalFetcher: &repository_fetcher.Local{
				Cake:              cake,
				DefaultRootFSPath: f.config.Containers.DefaultRootFS,
				IDProvider:        repository_fetcher.LayerIDProvider{},
			},
			RemoteFetcher: repository_fetcher.NewRemote(
				f.config.Docker.Registry,
				cake,
				distclient.NewDialer(f.config.Docker.InsecureRegistries),
				repository_fetcher.VerifyFunc(repository_fetcher.Verify),
			),
		},
	}

	rootFSNamespacer := &rootfs_provider.UidNamespacer{
		Translator: rootfs_provider.NewUidTranslator(
			f.uidMappings,
			f.gidMappings,
		),
	}

	retainer := cleaner.NewRetainer()
	ovenCleaner := cleaner.NewOvenCleaner(retainer,
		cleaner.NewThreshold(int64(f.config.Graph.CleanupThresholdInMegabytes)*1024*1024),
	)

	imageRetainer := &repository_fetcher.ImageRetainer{
		GraphRetainer:             retainer,
		DirectoryRootfsIDProvider: repository_fetcher.LayerIDProvider{},
		DockerImageIDFetcher:      repoFetcher,

		NamespaceCacheKey: rootFSNamespacer.CacheKey(),
		Logger:            logger,
	}

	// spawn off in a go function to avoid blocking startup
	// worst case is if an image is immediately created and deleted faster than
	// we can retain it we'll garbage collect it when we shouldn't. This
	// is an OK trade-off for not having garden startup block on dockerhub.
	go imageRetainer.Retain(f.config.Graph.PersistentImages)

	layerCreator := rootfs_provider.NewLayerCreator(cake, rootfs_provider.SimpleVolumeCreator{}, rootFSNamespacer)

	quotaManager := &quota_manager.AUFSQuotaManager{
		BaseSizer: quota_manager.NewAUFSBaseSizer(cake),
		DiffSizer: &quota_manager.AUFSDiffSizer{
			AUFSDiffPathFinder: quotaedGraphDriver,
		},
	}

	return rootfs_provider.NewCakeOrdinator(cake,
		repoFetcher,
		layerCreator,
		rootfs_provider.NewMetricsAdapter(quotaManager.GetUsage, quotaedGraphDriver.GetMntPath),
		ovenCleaner)
}
<commit_msg>Explicitly set mount options on /dev/shm<commit_after>package guardiancmd

import (
	"fmt"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"code.cloudfoundry.org/commandrunner"
	"code.cloudfoundry.org/commandrunner/linux_command_runner"
	"code.cloudfoundry.org/garden-shed/distclient"
	quotaed_aufs "code.cloudfoundry.org/garden-shed/docker_drivers/aufs"
	"code.cloudfoundry.org/garden-shed/layercake"
	"code.cloudfoundry.org/garden-shed/layercake/cleaner"
	"code.cloudfoundry.org/garden-shed/quota_manager"
	"code.cloudfoundry.org/garden-shed/repository_fetcher"
	"code.cloudfoundry.org/garden-shed/rootfs_provider"
	"code.cloudfoundry.org/guardian/gardener"
	"code.cloudfoundry.org/guardian/kawasaki"
	"code.cloudfoundry.org/guardian/kawasaki/dns"
	"code.cloudfoundry.org/guardian/logging"
	"code.cloudfoundry.org/guardian/rundmc"
	"code.cloudfoundry.org/guardian/rundmc/bundlerules"
	"code.cloudfoundry.org/guardian/rundmc/cgroups"
	"code.cloudfoundry.org/guardian/rundmc/execrunner/dadoo"
	"code.cloudfoundry.org/guardian/rundmc/preparerootfs"
	"code.cloudfoundry.org/guardian/rundmc/runrunc"
	"code.cloudfoundry.org/guardian/rundmc/signals"
	"code.cloudfoundry.org/idmapper"
	"code.cloudfoundry.org/lager"
	"github.com/docker/docker/daemon/graphdriver"
	"github.com/docker/docker/graph"
	"github.com/eapache/go-resiliency/retrier"
	specs "github.com/opencontainers/runtime-spec/specs-go"
)

type LinuxFactory struct {
	config           *ServerCommand
	commandRunner    commandrunner.CommandRunner
	signallerFactory *signals.SignallerFactory
	uidMappings      idmapper.MappingList
	gidMappings      idmapper.MappingList
}

func (cmd *ServerCommand) NewGardenFactory() GardenFactory {
	uidMappings, gidMappings := cmd.idMappings()
	return &LinuxFactory{
		config:           cmd,
		commandRunner:    linux_command_runner.New(),
		signallerFactory: &signals.SignallerFactory{PidGetter: wirePidfileReader()},
		uidMappings:      uidMappings,
		gidMappings:      gidMappings,
	}
}

func (f *LinuxFactory) CommandRunner() commandrunner.CommandRunner {
	return f.commandRunner
}

func (f *LinuxFactory) WireVolumizer(logger lager.Logger) gardener.Volumizer {
	if f.config.Image.Plugin.Path() != "" || f.config.Image.PrivilegedPlugin.Path() != "" {
		return f.config.wireImagePlugin(f.commandRunner, f.uidMappings.Map(0), f.gidMappings.Map(0))
	}

	if f.config.Graph.Dir == "" {
		return gardener.NoopVolumizer{}
	}

	shed := f.wireShed(logger)
	return gardener.NewVolumeProvider(shed, shed, gardener.CommandFactory(preparerootfs.Command), f.commandRunner, f.uidMappings.Map(0), f.gidMappings.Map(0))
}

func wireEnvFunc() runrunc.EnvFunc {
	return runrunc.EnvFunc(runrunc.UnixEnvFor)
}

func (f *LinuxFactory) WireMkdirer() runrunc.Mkdirer {
	if runningAsRoot() {
		return bundlerules.MkdirChowner{Command: preparerootfs.Command, CommandRunner: f.commandRunner}
	}

	return NoopMkdirer{}
}

type NoopMkdirer struct{}

func (NoopMkdirer) MkdirAs(rootFSPathFile string, uid, gid int, mode os.FileMode, recreate bool, path ...string) error {
	return nil
}

func (f *LinuxFactory) WireExecRunner(runMode string) runrunc.ExecRunner {
	return dadoo.NewExecRunner(
		f.config.Bin.Dadoo.Path(),
		f.config.Runtime.Plugin,
		f.signallerFactory,
		f.commandRunner,
		f.config.Containers.CleanupProcessDirsOnWait,
		runMode,
	)
}

func (f *LinuxFactory) WireCgroupsStarter(logger lager.Logger) gardener.Starter {
	return createCgroupsStarter(logger, f.config.Server.Tag, &cgroups.OSChowner{})
}

func (cmd *SetupCommand) WireCgroupsStarter(logger lager.Logger) gardener.Starter {
	return createCgroupsStarter(logger, cmd.Tag, &cgroups.OSChowner{UID: cmd.RootlessUID, GID: cmd.RootlessGID})
}

func createCgroupsStarter(logger lager.Logger, tag string, chowner cgroups.Chowner) gardener.Starter {
	cgroupsMountpoint := "/sys/fs/cgroup"
	gardenCgroup := "garden"
	if tag != "" {
		cgroupsMountpoint = filepath.Join(os.TempDir(), fmt.Sprintf("cgroups-%s", tag))
		gardenCgroup = fmt.Sprintf("%s-%s", gardenCgroup, tag)
	}

	return cgroups.NewStarter(logger, mustOpen("/proc/cgroups"), mustOpen("/proc/self/cgroup"),
		cgroupsMountpoint, gardenCgroup, allowedDevices, linux_command_runner.New(), chowner)
}

func (f *LinuxFactory) WireResolvConfigurer() kawasaki.DnsResolvConfigurer {
	return &kawasaki.ResolvConfigurer{
		HostsFileCompiler: &dns.HostsFileCompiler{},
		ResolvCompiler:    &dns.ResolvCompiler{},
		ResolvFilePath:    "/etc/resolv.conf",
		DepotDir:          f.config.Containers.Dir,
	}
}

func (f *LinuxFactory) WireRootfsFileCreator() rundmc.RootfsFileCreator {
	return preparerootfs.SymlinkRefusingFileCreator{}
}

func defaultBindMounts(binInitPath string) []specs.Mount {
	devptsGid := 0
	if runningAsRoot() {
		devptsGid = 5
	}

	return []specs.Mount{
		{Type: "sysfs", Source: "sysfs", Destination: "/sys", Options: []string{"nosuid", "noexec", "nodev", "ro"}},
		{Type: "tmpfs", Source: "tmpfs", Destination: "/dev/shm", Options: []string{"rw", "nodev", "relatime"}},
		{Type: "devpts", Source: "devpts", Destination: "/dev/pts",
			Options: []string{"nosuid", "noexec", "newinstance", fmt.Sprintf("gid=%d", devptsGid), "ptmxmode=0666", "mode=0620"}},
		{Type: "bind", Source: binInitPath, Destination: "/tmp/garden-init", Options: []string{"bind"}},
	}
}

func privilegedMounts() []specs.Mount {
	return []specs.Mount{
		{Type: "proc", Source: "proc", Destination: "/proc", Options: []string{"nosuid", "noexec", "nodev"}},
	}
}

func unprivilegedMounts() []specs.Mount {
	return []specs.Mount{
		{Type: "proc", Source: "proc", Destination: "/proc", Options: []string{"nosuid", "noexec", "nodev"}},
		{Type: "cgroup", Source: "cgroup", Destination: "/sys/fs/cgroup", Options: []string{"ro", "nosuid", "noexec", "nodev"}},
	}
}

func getPrivilegedDevices() []specs.LinuxDevice {
	return []specs.LinuxDevice{fuseDevice}
}

func bindMountPoints() []string {
	return []string{"/etc/hosts", "/etc/resolv.conf"}
}

func mustGetMaxValidUID() int {
	return idmapper.MustGetMaxValidUID()
}

func ensureServerSocketDoesNotLeak(socketFD uintptr) error {
	_, _, errNo := syscall.Syscall(syscall.SYS_FCNTL, socketFD, syscall.F_SETFD, syscall.FD_CLOEXEC)
	if errNo != 0 {
		return fmt.Errorf("setting cloexec on server socket: %s", errNo)
	}
	return nil
}

func createCmd() string {
	return "run"
}

func createCmdExtraArgs() []string {
	return []string{"--detach"}
}

func (f *LinuxFactory) wireShed(logger lager.Logger) *rootfs_provider.CakeOrdinator {

	graphRoot := f.config.Graph.Dir
	logger = logger.Session(gardener.VolumizerSession, lager.Data{"graphRoot": graphRoot})
	runner := &logging.Runner{CommandRunner: linux_command_runner.New(), Logger: logger}

	if err := os.MkdirAll(graphRoot, 0755); err != nil {
		logger.Fatal("failed-to-create-graph-directory", err)
	}

	dockerGraphDriver, err := graphdriver.New(graphRoot, nil)
	if err != nil {
		logger.Fatal("failed-to-construct-graph-driver", err)
	}

	backingStoresPath := filepath.Join(graphRoot, "backing_stores")
	if mkdirErr := os.MkdirAll(backingStoresPath, 0660); mkdirErr != nil {
		logger.Fatal("failed-to-mkdir-backing-stores", mkdirErr)
	}

	quotaedGraphDriver := &quotaed_aufs.QuotaedDriver{
		GraphDriver: dockerGraphDriver,
		Unmount:     quotaed_aufs.Unmount,
		BackingStoreMgr: &quotaed_aufs.BackingStore{
			RootPath: backingStoresPath,
			Logger:   logger.Session("backing-store-mgr"),
		},
		LoopMounter: &quotaed_aufs.Loop{
			Retrier: retrier.New(retrier.ConstantBackoff(200, 500*time.Millisecond), nil),
			Logger:  logger.Session("loop-mounter"),
		},
		Retrier:  retrier.New(retrier.ConstantBackoff(200, 500*time.Millisecond), nil),
		RootPath: graphRoot,
		Logger:   logger.Session("quotaed-driver"),
	}

	dockerGraph, err := graph.NewGraph(graphRoot, quotaedGraphDriver)
	if err != nil {
		logger.Fatal("failed-to-construct-graph", err)
	}

	var cake layercake.Cake = &layercake.Docker{
		Graph:  dockerGraph,
		Driver: quotaedGraphDriver,
	}

	if cake.DriverName() == "aufs" {
		cake = &layercake.AufsCake{
			Cake:      cake,
			Runner:    runner,
			GraphRoot: graphRoot,
		}
	}

	repoFetcher := repository_fetcher.Retryable{
		RepositoryFetcher: &repository_fetcher.CompositeFetcher{
			LocalFetcher: &repository_fetcher.Local{
				Cake:              cake,
				DefaultRootFSPath: f.config.Containers.DefaultRootFS,
				IDProvider:        repository_fetcher.LayerIDProvider{},
			},
			RemoteFetcher: repository_fetcher.NewRemote(
				f.config.Docker.Registry,
				cake,
				distclient.NewDialer(f.config.Docker.InsecureRegistries),
				repository_fetcher.VerifyFunc(repository_fetcher.Verify),
			),
		},
	}

	rootFSNamespacer := &rootfs_provider.UidNamespacer{
		Translator: rootfs_provider.NewUidTranslator(
			f.uidMappings,
			f.gidMappings,
		),
	}

	retainer := cleaner.NewRetainer()
	ovenCleaner := cleaner.NewOvenCleaner(retainer,
		cleaner.NewThreshold(int64(f.config.Graph.CleanupThresholdInMegabytes)*1024*1024),
	)

	imageRetainer := &repository_fetcher.ImageRetainer{
		GraphRetainer:             retainer,
		DirectoryRootfsIDProvider: repository_fetcher.LayerIDProvider{},
		DockerImageIDFetcher:      repoFetcher,

		NamespaceCacheKey: rootFSNamespacer.CacheKey(),
		Logger:            logger,
	}

	// spawn off in a go function to avoid blocking startup
	// worst case is if an image is immediately created and deleted faster than
	// we can retain it we'll garbage collect it when we shouldn't. This
	// is an OK trade-off for not having garden startup block on dockerhub.
	go imageRetainer.Retain(f.config.Graph.PersistentImages)

	layerCreator := rootfs_provider.NewLayerCreator(cake, rootfs_provider.SimpleVolumeCreator{}, rootFSNamespacer)

	quotaManager := &quota_manager.AUFSQuotaManager{
		BaseSizer: quota_manager.NewAUFSBaseSizer(cake),
		DiffSizer: &quota_manager.AUFSDiffSizer{
			AUFSDiffPathFinder: quotaedGraphDriver,
		},
	}

	return rootfs_provider.NewCakeOrdinator(cake,
		repoFetcher,
		layerCreator,
		rootfs_provider.NewMetricsAdapter(quotaManager.GetUsage, quotaedGraphDriver.GetMntPath),
		ovenCleaner)
}
<|endoftext|>


<commit_before>package html

import (
	"fmt"
	"io"
	"syscall"
	"time"
)

var startTime time.Time = time.Now()

func writeHeader(writer io.Writer) {
	fmt.Fprintf(writer, "Start time: %s
\n", startTime)
	uptime := time.Since(startTime)
	fmt.Fprintf(writer, "Uptime: %s
\n", uptime)
	var rusage syscall.Rusage
	syscall.Getrusage(syscall.RUSAGE_SELF, &rusage)
	cpuTime := rusage.Utime.Sec + rusage.Stime.Sec
	fmt.Fprintf(writer, "CPU Time: %d%%
\n",
		cpuTime*100/int64(uptime.Seconds()))
}
<commit_msg>Add allocated memory to HTML status pages.<commit_after>package html

import (
	"fmt"
	"github.com/Symantec/Dominator/lib/format"
	"io"
	"runtime"
	"syscall"
	"time"
)

var startTime time.Time = time.Now()

func writeHeader(writer io.Writer) {
	fmt.Fprintf(writer, "Start time: %s
\n", startTime)
	uptime := time.Since(startTime)
	fmt.Fprintf(writer, "Uptime: %s
\n", uptime)
	var rusage syscall.Rusage
	syscall.Getrusage(syscall.RUSAGE_SELF, &rusage)
	cpuTime := rusage.Utime.Sec + rusage.Stime.Sec
	fmt.Fprintf(writer, "CPU Time: %d%%
\n",
		cpuTime*100/int64(uptime.Seconds()))
	var memStats runtime.MemStats
	runtime.ReadMemStats(&memStats)
	fmt.Fprintf(writer, "Allocated memory: %s
\n",
		format.FormatBytes(memStats.Alloc))
}
<|endoftext|>