<commit_before>package accesscontrol

import (
	"context"
	"fmt"
	"strings"

	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/registry"
	"github.com/grafana/grafana/pkg/services/org"
	"github.com/grafana/grafana/pkg/services/user"
	"github.com/grafana/grafana/pkg/setting"
)

type AccessControl interface {
	// Evaluate evaluates access to the given resources.
	Evaluate(ctx context.Context, user *user.SignedInUser, evaluator Evaluator) (bool, error)
	// RegisterScopeAttributeResolver allows the caller to register a scope resolver for a
	// specific scope prefix (ex: datasources:name:)
	RegisterScopeAttributeResolver(prefix string, resolver ScopeAttributeResolver)
	//IsDisabled returns if access control is enabled or not
	IsDisabled() bool
}

type Service interface {
	registry.ProvidesUsageStats
	// GetUserPermissions returns user permissions with only action and scope fields set.
	GetUserPermissions(ctx context.Context, user *user.SignedInUser, options Options) ([]Permission, error)
	// DeleteUserPermissions removes all permissions user has in org and all permission to that user
	// If orgID is set to 0 remove permissions from all orgs
	DeleteUserPermissions(ctx context.Context, orgID, userID int64) error
	// DeclareFixedRoles allows the caller to declare, to the service, fixed roles and their
	// assignments to organization roles ("Viewer", "Editor", "Admin") or "Grafana Admin"
	DeclareFixedRoles(registrations ...RoleRegistration) error
	//IsDisabled returns if access control is enabled or not
	IsDisabled() bool
}

type RoleRegistry interface {
	// RegisterFixedRoles registers all roles declared to AccessControl
	RegisterFixedRoles(ctx context.Context) error
}

type Options struct {
	ReloadCache bool
}

type TeamPermissionsService interface {
	GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]ResourcePermission, error)
	SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error)
}

type FolderPermissionsService interface {
	PermissionsService
}

type DashboardPermissionsService interface {
	PermissionsService
}

type DatasourcePermissionsService interface {
	PermissionsService
}

type ServiceAccountPermissionsService interface {
	PermissionsService
}

type PermissionsService interface {
	// GetPermissions returns all permissions for given resourceID
	GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]ResourcePermission, error)
	// SetUserPermission sets permission on resource for a user
	SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error)
	// SetTeamPermission sets permission on resource for a team
	SetTeamPermission(ctx context.Context, orgID, teamID int64, resourceID, permission string) (*ResourcePermission, error)
	// SetBuiltInRolePermission sets permission on resource for a built-in role (Admin, Editor, Viewer)
	SetBuiltInRolePermission(ctx context.Context, orgID int64, builtInRole string, resourceID string, permission string) (*ResourcePermission, error)
	// SetPermissions sets several permissions on resource for either built-in role, team or user
	SetPermissions(ctx context.Context, orgID int64, resourceID string, commands ...SetResourcePermissionCommand) ([]ResourcePermission, error)
	// MapActions will map actions for a ResourcePermissions to it's "friendly" name configured in PermissionsToActions map.
	MapActions(permission ResourcePermission) string
}

type User struct {
	ID         int64
	IsExternal bool
}

// HasGlobalAccess checks user access with globally assigned permissions only
func HasGlobalAccess(ac AccessControl, service Service, c *models.ReqContext) func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
	return func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
		if ac.IsDisabled() {
			return fallback(c)
		}

		userCopy := *c.SignedInUser
		userCopy.OrgID = GlobalOrgID
		userCopy.OrgRole = ""
		userCopy.OrgName = ""
		if userCopy.Permissions[GlobalOrgID] == nil {
			permissions, err := service.GetUserPermissions(c.Req.Context(), &userCopy, Options{})
			if err != nil {
				c.Logger.Error("failed fetching permissions for user", "userID", userCopy.UserID, "error", err)
			}
			userCopy.Permissions[GlobalOrgID] = GroupScopesByAction(permissions)
		}

		hasAccess, err := ac.Evaluate(c.Req.Context(), &userCopy, evaluator)
		if err != nil {
			c.Logger.Error("Error from access control system", "error", err)
			return false
		}

		// set on user so we don't fetch global permissions every time this is called
		c.SignedInUser.Permissions[GlobalOrgID] = userCopy.Permissions[GlobalOrgID]

		return hasAccess
	}
}

func HasAccess(ac AccessControl, c *models.ReqContext) func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
	return func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
		if ac.IsDisabled() {
			return fallback(c)
		}

		hasAccess, err := ac.Evaluate(c.Req.Context(), c.SignedInUser, evaluator)
		if err != nil {
			c.Logger.Error("Error from access control system", "error", err)
			return false
		}

		return hasAccess
	}
}

var ReqSignedIn = func(c *models.ReqContext) bool {
	return c.IsSignedIn
}

var ReqGrafanaAdmin = func(c *models.ReqContext) bool {
	return c.IsGrafanaAdmin
}

// ReqViewer returns true if the current user has org.RoleViewer. Note: this can be anonymous user as well
var ReqViewer = func(c *models.ReqContext) bool {
	return c.OrgRole.Includes(org.RoleViewer)
}

var ReqOrgAdmin = func(c *models.ReqContext) bool {
	return c.OrgRole == org.RoleAdmin
}

var ReqOrgAdminOrEditor = func(c *models.ReqContext) bool {
	return c.OrgRole == org.RoleAdmin || c.OrgRole == org.RoleEditor
}

// ReqHasRole generates a fallback to check whether the user has a role
// Note that while ReqOrgAdmin returns false for a Grafana Admin / Viewer, ReqHasRole(org.RoleAdmin) will return true
func ReqHasRole(role org.RoleType) func(c *models.ReqContext) bool {
	return func(c *models.ReqContext) bool { return c.HasRole(role) }
}

func BuildPermissionsMap(permissions []Permission) map[string]bool {
	permissionsMap := make(map[string]bool)
	for _, p := range permissions {
		permissionsMap[p.Action] = true
	}

	return permissionsMap
}

// GroupScopesByAction will group scopes on action
func GroupScopesByAction(permissions []Permission) map[string][]string {
	m := make(map[string][]string)
	for _, p := range permissions {
		m[p.Action] = append(m[p.Action], p.Scope)
	}
	return m
}

func ValidateScope(scope string) bool {
	prefix, last := scope[:len(scope)-1], scope[len(scope)-1]
	// verify that last char is either ':' or '/' if last character of scope is '*'
	if len(prefix) > 0 && last == '*' {
		lastChar := prefix[len(prefix)-1]
		if lastChar != ':' && lastChar != '/' {
			return false
		}
	}
	return !strings.ContainsAny(prefix, "*?")
}

func ManagedUserRoleName(userID int64) string {
	return fmt.Sprintf("managed:users:%d:permissions", userID)
}

func ManagedTeamRoleName(teamID int64) string {
	return fmt.Sprintf("managed:teams:%d:permissions", teamID)
}

func ManagedBuiltInRoleName(builtInRole string) string {
	return fmt.Sprintf("managed:builtins:%s:permissions", strings.ToLower(builtInRole))
}

func IsDisabled(cfg *setting.Cfg) bool {
	return !cfg.RBACEnabled
}

// GetOrgRoles returns legacy org roles for a user
func GetOrgRoles(user *user.SignedInUser) []string {
	roles := []string{string(user.OrgRole)}

	if user.IsGrafanaAdmin {
		roles = append(roles, RoleGrafanaAdmin)
	}

	return roles
}

func BackgroundUser(name string, orgID int64, role org.RoleType, permissions []Permission) *user.SignedInUser {
	return &user.SignedInUser{
		OrgID:   orgID,
		OrgRole: role,
		Login:   "grafana_" + name,
		Permissions: map[int64]map[string][]string{
			orgID: GroupScopesByAction(permissions),
		},
	}
}
<commit_msg>Auth: reduce allocs (#55410)<commit_after>package accesscontrol

import (
	"context"
	"fmt"
	"strings"

	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/registry"
	"github.com/grafana/grafana/pkg/services/org"
	"github.com/grafana/grafana/pkg/services/user"
	"github.com/grafana/grafana/pkg/setting"
)

type AccessControl interface {
	// Evaluate evaluates access to the given resources.
	Evaluate(ctx context.Context, user *user.SignedInUser, evaluator Evaluator) (bool, error)
	// RegisterScopeAttributeResolver allows the caller to register a scope resolver for a
	// specific scope prefix (ex: datasources:name:)
	RegisterScopeAttributeResolver(prefix string, resolver ScopeAttributeResolver)
	//IsDisabled returns if access control is enabled or not
	IsDisabled() bool
}

type Service interface {
	registry.ProvidesUsageStats
	// GetUserPermissions returns user permissions with only action and scope fields set.
	GetUserPermissions(ctx context.Context, user *user.SignedInUser, options Options) ([]Permission, error)
	// DeleteUserPermissions removes all permissions user has in org and all permission to that user
	// If orgID is set to 0 remove permissions from all orgs
	DeleteUserPermissions(ctx context.Context, orgID, userID int64) error
	// DeclareFixedRoles allows the caller to declare, to the service, fixed roles and their
	// assignments to organization roles ("Viewer", "Editor", "Admin") or "Grafana Admin"
	DeclareFixedRoles(registrations ...RoleRegistration) error
	//IsDisabled returns if access control is enabled or not
	IsDisabled() bool
}

type RoleRegistry interface {
	// RegisterFixedRoles registers all roles declared to AccessControl
	RegisterFixedRoles(ctx context.Context) error
}

type Options struct {
	ReloadCache bool
}

type TeamPermissionsService interface {
	GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]ResourcePermission, error)
	SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error)
}

type FolderPermissionsService interface {
	PermissionsService
}

type DashboardPermissionsService interface {
	PermissionsService
}

type DatasourcePermissionsService interface {
	PermissionsService
}

type ServiceAccountPermissionsService interface {
	PermissionsService
}

type PermissionsService interface {
	// GetPermissions returns all permissions for given resourceID
	GetPermissions(ctx context.Context, user *user.SignedInUser, resourceID string) ([]ResourcePermission, error)
	// SetUserPermission sets permission on resource for a user
	SetUserPermission(ctx context.Context, orgID int64, user User, resourceID, permission string) (*ResourcePermission, error)
	// SetTeamPermission sets permission on resource for a team
	SetTeamPermission(ctx context.Context, orgID, teamID int64, resourceID, permission string) (*ResourcePermission, error)
	// SetBuiltInRolePermission sets permission on resource for a built-in role (Admin, Editor, Viewer)
	SetBuiltInRolePermission(ctx context.Context, orgID int64, builtInRole string, resourceID string, permission string) (*ResourcePermission, error)
	// SetPermissions sets several permissions on resource for either built-in role, team or user
	SetPermissions(ctx context.Context, orgID int64, resourceID string, commands ...SetResourcePermissionCommand) ([]ResourcePermission, error)
	// MapActions will map actions for a ResourcePermissions to it's "friendly" name configured in PermissionsToActions map.
	MapActions(permission ResourcePermission) string
}

type User struct {
	ID         int64
	IsExternal bool
}

// HasGlobalAccess checks user access with globally assigned permissions only
func HasGlobalAccess(ac AccessControl, service Service, c *models.ReqContext) func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
	return func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
		if ac.IsDisabled() {
			return fallback(c)
		}

		userCopy := *c.SignedInUser
		userCopy.OrgID = GlobalOrgID
		userCopy.OrgRole = ""
		userCopy.OrgName = ""
		if userCopy.Permissions[GlobalOrgID] == nil {
			permissions, err := service.GetUserPermissions(c.Req.Context(), &userCopy, Options{})
			if err != nil {
				c.Logger.Error("failed fetching permissions for user", "userID", userCopy.UserID, "error", err)
			}
			userCopy.Permissions[GlobalOrgID] = GroupScopesByAction(permissions)
		}

		hasAccess, err := ac.Evaluate(c.Req.Context(), &userCopy, evaluator)
		if err != nil {
			c.Logger.Error("Error from access control system", "error", err)
			return false
		}

		// set on user so we don't fetch global permissions every time this is called
		c.SignedInUser.Permissions[GlobalOrgID] = userCopy.Permissions[GlobalOrgID]

		return hasAccess
	}
}

func HasAccess(ac AccessControl, c *models.ReqContext) func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
	return func(fallback func(*models.ReqContext) bool, evaluator Evaluator) bool {
		if ac.IsDisabled() {
			return fallback(c)
		}

		hasAccess, err := ac.Evaluate(c.Req.Context(), c.SignedInUser, evaluator)
		if err != nil {
			c.Logger.Error("Error from access control system", "error", err)
			return false
		}

		return hasAccess
	}
}

var ReqSignedIn = func(c *models.ReqContext) bool {
	return c.IsSignedIn
}

var ReqGrafanaAdmin = func(c *models.ReqContext) bool {
	return c.IsGrafanaAdmin
}

// ReqViewer returns true if the current user has org.RoleViewer. Note: this can be anonymous user as well
var ReqViewer = func(c *models.ReqContext) bool {
	return c.OrgRole.Includes(org.RoleViewer)
}

var ReqOrgAdmin = func(c *models.ReqContext) bool {
	return c.OrgRole == org.RoleAdmin
}

var ReqOrgAdminOrEditor = func(c *models.ReqContext) bool {
	return c.OrgRole == org.RoleAdmin || c.OrgRole == org.RoleEditor
}

// ReqHasRole generates a fallback to check whether the user has a role
// Note that while ReqOrgAdmin returns false for a Grafana Admin / Viewer, ReqHasRole(org.RoleAdmin) will return true
func ReqHasRole(role org.RoleType) func(c *models.ReqContext) bool {
	return func(c *models.ReqContext) bool { return c.HasRole(role) }
}

func BuildPermissionsMap(permissions []Permission) map[string]bool {
	permissionsMap := make(map[string]bool)
	for _, p := range permissions {
		permissionsMap[p.Action] = true
	}

	return permissionsMap
}

// GroupScopesByAction will group scopes on action
func GroupScopesByAction(permissions []Permission) map[string][]string {
	m := make(map[string][]string)
	for i := range permissions {
		m[permissions[i].Action] = append(m[permissions[i].Action], permissions[i].Scope)
	}
	return m
}

func ValidateScope(scope string) bool {
	prefix, last := scope[:len(scope)-1], scope[len(scope)-1]
	// verify that last char is either ':' or '/' if last character of scope is '*'
	if len(prefix) > 0 && last == '*' {
		lastChar := prefix[len(prefix)-1]
		if lastChar != ':' && lastChar != '/' {
			return false
		}
	}
	return !strings.ContainsAny(prefix, "*?")
}

func ManagedUserRoleName(userID int64) string {
	return fmt.Sprintf("managed:users:%d:permissions", userID)
}

func ManagedTeamRoleName(teamID int64) string {
	return fmt.Sprintf("managed:teams:%d:permissions", teamID)
}

func ManagedBuiltInRoleName(builtInRole string) string {
	return fmt.Sprintf("managed:builtins:%s:permissions", strings.ToLower(builtInRole))
}

func IsDisabled(cfg *setting.Cfg) bool {
	return !cfg.RBACEnabled
}

// GetOrgRoles returns legacy org roles for a user
func GetOrgRoles(user *user.SignedInUser) []string {
	roles := []string{string(user.OrgRole)}

	if user.IsGrafanaAdmin {
		roles = append(roles, RoleGrafanaAdmin)
	}

	return roles
}

func BackgroundUser(name string, orgID int64, role org.RoleType, permissions []Permission) *user.SignedInUser {
	return &user.SignedInUser{
		OrgID:   orgID,
		OrgRole: role,
		Login:   "grafana_" + name,
		Permissions: map[int64]map[string][]string{
			orgID: GroupScopesByAction(permissions),
		},
	}
}
<|endoftext|>


<commit_before>package sqlstore

import (
	"bytes"
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/bus"
	m "github.com/grafana/grafana/pkg/models"
)

func init() {
	bus.AddHandler("sql", GetAlertNotifications)
	bus.AddHandler("sql", CreateAlertNotificationCommand)
	bus.AddHandler("sql", UpdateAlertNotification)
	bus.AddHandler("sql", DeleteAlertNotification)
	bus.AddHandler("sql", GetAlertNotificationsToSend)
	bus.AddHandler("sql", GetAllAlertNotifications)
	bus.AddHandlerCtx("sql", RecordNotificationJournal)
	bus.AddHandlerCtx("sql", GetLatestNotification)
	bus.AddHandlerCtx("sql", CleanNotificationJournal)
}

func DeleteAlertNotification(cmd *m.DeleteAlertNotificationCommand) error {
	return inTransaction(func(sess *DBSession) error {
		sql := "DELETE FROM alert_notification WHERE alert_notification.org_id = ? AND alert_notification.id = ?"
		_, err := sess.Exec(sql, cmd.OrgId, cmd.Id)
		return err
	})
}

func GetAlertNotifications(query *m.GetAlertNotificationsQuery) error {
	return getAlertNotificationInternal(query, newSession())
}

func GetAllAlertNotifications(query *m.GetAllAlertNotificationsQuery) error {
	results := make([]*m.AlertNotification, 0)
	if err := x.Where("org_id = ?", query.OrgId).Find(&results); err != nil {
		return err
	}

	query.Result = results
	return nil
}

func GetAlertNotificationsToSend(query *m.GetAlertNotificationsToSendQuery) error {
	var sql bytes.Buffer
	params := make([]interface{}, 0)

	sql.WriteString(`SELECT
										alert_notification.id,
										alert_notification.org_id,
										alert_notification.name,
										alert_notification.type,
										alert_notification.created,
										alert_notification.updated,
										alert_notification.settings,
										alert_notification.is_default,
										alert_notification.send_reminder,
										alert_notification.frequency
										FROM alert_notification
	  							`)

	sql.WriteString(` WHERE alert_notification.org_id = ?`)
	params = append(params, query.OrgId)

	sql.WriteString(` AND ((alert_notification.is_default = ?)`)
	params = append(params, dialect.BooleanStr(true))
	if len(query.Ids) > 0 {
		sql.WriteString(` OR alert_notification.id IN (?` + strings.Repeat(",?", len(query.Ids)-1) + ")")
		for _, v := range query.Ids {
			params = append(params, v)
		}
	}
	sql.WriteString(`)`)

	results := make([]*m.AlertNotification, 0)
	if err := x.SQL(sql.String(), params...).Find(&results); err != nil {
		return err
	}

	query.Result = results
	return nil
}

func getAlertNotificationInternal(query *m.GetAlertNotificationsQuery, sess *DBSession) error {
	var sql bytes.Buffer
	params := make([]interface{}, 0)

	sql.WriteString(`SELECT
										alert_notification.id,
										alert_notification.org_id,
										alert_notification.name,
										alert_notification.type,
										alert_notification.created,
										alert_notification.updated,
										alert_notification.settings,
										alert_notification.is_default,
										alert_notification.send_reminder,
										alert_notification.frequency
										FROM alert_notification
	  							`)

	sql.WriteString(` WHERE alert_notification.org_id = ?`)
	params = append(params, query.OrgId)

	if query.Name != "" || query.Id != 0 {
		if query.Name != "" {
			sql.WriteString(` AND alert_notification.name = ?`)
			params = append(params, query.Name)
		}

		if query.Id != 0 {
			sql.WriteString(` AND alert_notification.id = ?`)
			params = append(params, query.Id)
		}
	}

	results := make([]*m.AlertNotification, 0)
	if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
		return err
	}

	if len(results) == 0 {
		query.Result = nil
	} else {
		query.Result = results[0]
	}

	return nil
}

func CreateAlertNotificationCommand(cmd *m.CreateAlertNotificationCommand) error {
	return inTransaction(func(sess *DBSession) error {
		existingQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
		err := getAlertNotificationInternal(existingQuery, sess)

		if err != nil {
			return err
		}

		if existingQuery.Result != nil {
			return fmt.Errorf("Alert notification name %s already exists", cmd.Name)
		}

		var frequency time.Duration
		if cmd.SendReminder {
			if cmd.Frequency == "" {
				return m.ErrNotificationFrequencyNotFound
			}

			frequency, err = time.ParseDuration(cmd.Frequency)
			if err != nil {
				return err
			}
		}

		alertNotification := &m.AlertNotification{
			OrgId:        cmd.OrgId,
			Name:         cmd.Name,
			Type:         cmd.Type,
			Settings:     cmd.Settings,
			SendReminder: cmd.SendReminder,
			Frequency:    frequency,
			Created:      time.Now(),
			Updated:      time.Now(),
			IsDefault:    cmd.IsDefault,
		}

		if _, err = sess.MustCols("send_reminder").Insert(alertNotification); err != nil {
			return err
		}

		cmd.Result = alertNotification
		return nil
	})
}

func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error {
	return inTransaction(func(sess *DBSession) (err error) {
		current := m.AlertNotification{}

		if _, err = sess.ID(cmd.Id).Get(&current); err != nil {
			return err
		}

		// check if name exists
		sameNameQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
		if err := getAlertNotificationInternal(sameNameQuery, sess); err != nil {
			return err
		}

		if sameNameQuery.Result != nil && sameNameQuery.Result.Id != current.Id {
			return fmt.Errorf("Alert notification name %s already exists", cmd.Name)
		}

		current.Updated = time.Now()
		current.Settings = cmd.Settings
		current.Name = cmd.Name
		current.Type = cmd.Type
		current.IsDefault = cmd.IsDefault
		current.SendReminder = cmd.SendReminder

		if current.SendReminder {
			if cmd.Frequency == "" {
				return m.ErrNotificationFrequencyNotFound
			}

			frequency, err := time.ParseDuration(cmd.Frequency)
			if err != nil {
				return err
			}

			current.Frequency = frequency
		}

		sess.UseBool("is_default", "send_reminder")

		if affected, err := sess.ID(cmd.Id).Update(current); err != nil {
			return err
		} else if affected == 0 {
			return fmt.Errorf("Could not update alert notification")
		}

		cmd.Result = &current
		return nil
	})
}

func RecordNotificationJournal(ctx context.Context, cmd *m.RecordNotificationJournalCommand) error {
	return inTransactionCtx(ctx, func(sess *DBSession) error {
		journalEntry := &m.AlertNotificationJournal{
			OrgId:      cmd.OrgId,
			AlertId:    cmd.AlertId,
			NotifierId: cmd.NotifierId,
			SentAt:     cmd.SentAt,
			Success:    cmd.Success,
		}

		if _, err := sess.Insert(journalEntry); err != nil {
			return err
		}

		return nil
	})
}

func GetLatestNotification(ctx context.Context, cmd *m.GetLatestNotificationQuery) error {
	return inTransactionCtx(ctx, func(sess *DBSession) error {
		nj := &m.AlertNotificationJournal{}

		_, err := sess.Desc("alert_notification_journal.sent_at").
			Limit(1).
			Where("alert_notification_journal.org_id = ? AND alert_notification_journal.alert_id = ? AND alert_notification_journal.notifier_id = ?", cmd.OrgId, cmd.AlertId, cmd.NotifierId).Get(nj)

		if err != nil {
			return err
		}

		if nj.AlertId == 0 && nj.Id == 0 && nj.NotifierId == 0 && nj.OrgId == 0 {
			return m.ErrJournalingNotFound
		}

		cmd.Result = nj
		return nil
	})
}

func CleanNotificationJournal(ctx context.Context, cmd *m.CleanNotificationJournalCommand) error {
	return inTransactionCtx(ctx, func(sess *DBSession) error {
		sql := "DELETE FROM alert_notification_journal WHERE alert_notification_journal.org_id = ? AND alert_notification_journal.alert_id = ? AND alert_notification_journal.notifier_id = ?"
		_, err := sess.Exec(sql, cmd.OrgId, cmd.AlertId, cmd.NotifierId)
		return err
	})
}
<commit_msg>pkg/services/sqlstore/alert_notification.go: Simplify err check<commit_after>package sqlstore

import (
	"bytes"
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/grafana/grafana/pkg/bus"
	m "github.com/grafana/grafana/pkg/models"
)

func init() {
	bus.AddHandler("sql", GetAlertNotifications)
	bus.AddHandler("sql", CreateAlertNotificationCommand)
	bus.AddHandler("sql", UpdateAlertNotification)
	bus.AddHandler("sql", DeleteAlertNotification)
	bus.AddHandler("sql", GetAlertNotificationsToSend)
	bus.AddHandler("sql", GetAllAlertNotifications)
	bus.AddHandlerCtx("sql", RecordNotificationJournal)
	bus.AddHandlerCtx("sql", GetLatestNotification)
	bus.AddHandlerCtx("sql", CleanNotificationJournal)
}

func DeleteAlertNotification(cmd *m.DeleteAlertNotificationCommand) error {
	return inTransaction(func(sess *DBSession) error {
		sql := "DELETE FROM alert_notification WHERE alert_notification.org_id = ? AND alert_notification.id = ?"
		_, err := sess.Exec(sql, cmd.OrgId, cmd.Id)
		return err
	})
}

func GetAlertNotifications(query *m.GetAlertNotificationsQuery) error {
	return getAlertNotificationInternal(query, newSession())
}

func GetAllAlertNotifications(query *m.GetAllAlertNotificationsQuery) error {
	results := make([]*m.AlertNotification, 0)
	if err := x.Where("org_id = ?", query.OrgId).Find(&results); err != nil {
		return err
	}

	query.Result = results
	return nil
}

func GetAlertNotificationsToSend(query *m.GetAlertNotificationsToSendQuery) error {
	var sql bytes.Buffer
	params := make([]interface{}, 0)

	sql.WriteString(`SELECT
										alert_notification.id,
										alert_notification.org_id,
										alert_notification.name,
										alert_notification.type,
										alert_notification.created,
										alert_notification.updated,
										alert_notification.settings,
										alert_notification.is_default,
										alert_notification.send_reminder,
										alert_notification.frequency
										FROM alert_notification
	  							`)

	sql.WriteString(` WHERE alert_notification.org_id = ?`)
	params = append(params, query.OrgId)

	sql.WriteString(` AND ((alert_notification.is_default = ?)`)
	params = append(params, dialect.BooleanStr(true))
	if len(query.Ids) > 0 {
		sql.WriteString(` OR alert_notification.id IN (?` + strings.Repeat(",?", len(query.Ids)-1) + ")")
		for _, v := range query.Ids {
			params = append(params, v)
		}
	}
	sql.WriteString(`)`)

	results := make([]*m.AlertNotification, 0)
	if err := x.SQL(sql.String(), params...).Find(&results); err != nil {
		return err
	}

	query.Result = results
	return nil
}

func getAlertNotificationInternal(query *m.GetAlertNotificationsQuery, sess *DBSession) error {
	var sql bytes.Buffer
	params := make([]interface{}, 0)

	sql.WriteString(`SELECT
										alert_notification.id,
										alert_notification.org_id,
										alert_notification.name,
										alert_notification.type,
										alert_notification.created,
										alert_notification.updated,
										alert_notification.settings,
										alert_notification.is_default,
										alert_notification.send_reminder,
										alert_notification.frequency
										FROM alert_notification
	  							`)

	sql.WriteString(` WHERE alert_notification.org_id = ?`)
	params = append(params, query.OrgId)

	if query.Name != "" || query.Id != 0 {
		if query.Name != "" {
			sql.WriteString(` AND alert_notification.name = ?`)
			params = append(params, query.Name)
		}

		if query.Id != 0 {
			sql.WriteString(` AND alert_notification.id = ?`)
			params = append(params, query.Id)
		}
	}

	results := make([]*m.AlertNotification, 0)
	if err := sess.SQL(sql.String(), params...).Find(&results); err != nil {
		return err
	}

	if len(results) == 0 {
		query.Result = nil
	} else {
		query.Result = results[0]
	}

	return nil
}

func CreateAlertNotificationCommand(cmd *m.CreateAlertNotificationCommand) error {
	return inTransaction(func(sess *DBSession) error {
		existingQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
		err := getAlertNotificationInternal(existingQuery, sess)

		if err != nil {
			return err
		}

		if existingQuery.Result != nil {
			return fmt.Errorf("Alert notification name %s already exists", cmd.Name)
		}

		var frequency time.Duration
		if cmd.SendReminder {
			if cmd.Frequency == "" {
				return m.ErrNotificationFrequencyNotFound
			}

			frequency, err = time.ParseDuration(cmd.Frequency)
			if err != nil {
				return err
			}
		}

		alertNotification := &m.AlertNotification{
			OrgId:        cmd.OrgId,
			Name:         cmd.Name,
			Type:         cmd.Type,
			Settings:     cmd.Settings,
			SendReminder: cmd.SendReminder,
			Frequency:    frequency,
			Created:      time.Now(),
			Updated:      time.Now(),
			IsDefault:    cmd.IsDefault,
		}

		if _, err = sess.MustCols("send_reminder").Insert(alertNotification); err != nil {
			return err
		}

		cmd.Result = alertNotification
		return nil
	})
}

func UpdateAlertNotification(cmd *m.UpdateAlertNotificationCommand) error {
	return inTransaction(func(sess *DBSession) (err error) {
		current := m.AlertNotification{}

		if _, err = sess.ID(cmd.Id).Get(&current); err != nil {
			return err
		}

		// check if name exists
		sameNameQuery := &m.GetAlertNotificationsQuery{OrgId: cmd.OrgId, Name: cmd.Name}
		if err := getAlertNotificationInternal(sameNameQuery, sess); err != nil {
			return err
		}

		if sameNameQuery.Result != nil && sameNameQuery.Result.Id != current.Id {
			return fmt.Errorf("Alert notification name %s already exists", cmd.Name)
		}

		current.Updated = time.Now()
		current.Settings = cmd.Settings
		current.Name = cmd.Name
		current.Type = cmd.Type
		current.IsDefault = cmd.IsDefault
		current.SendReminder = cmd.SendReminder

		if current.SendReminder {
			if cmd.Frequency == "" {
				return m.ErrNotificationFrequencyNotFound
			}

			frequency, err := time.ParseDuration(cmd.Frequency)
			if err != nil {
				return err
			}

			current.Frequency = frequency
		}

		sess.UseBool("is_default", "send_reminder")

		if affected, err := sess.ID(cmd.Id).Update(current); err != nil {
			return err
		} else if affected == 0 {
			return fmt.Errorf("Could not update alert notification")
		}

		cmd.Result = &current
		return nil
	})
}

func RecordNotificationJournal(ctx context.Context, cmd *m.RecordNotificationJournalCommand) error {
	return inTransactionCtx(ctx, func(sess *DBSession) error {
		journalEntry := &m.AlertNotificationJournal{
			OrgId:      cmd.OrgId,
			AlertId:    cmd.AlertId,
			NotifierId: cmd.NotifierId,
			SentAt:     cmd.SentAt,
			Success:    cmd.Success,
		}

		_, err := sess.Insert(journalEntry)
		return err
	})
}

func GetLatestNotification(ctx context.Context, cmd *m.GetLatestNotificationQuery) error {
	return inTransactionCtx(ctx, func(sess *DBSession) error {
		nj := &m.AlertNotificationJournal{}

		_, err := sess.Desc("alert_notification_journal.sent_at").
			Limit(1).
			Where("alert_notification_journal.org_id = ? AND alert_notification_journal.alert_id = ? AND alert_notification_journal.notifier_id = ?", cmd.OrgId, cmd.AlertId, cmd.NotifierId).Get(nj)

		if err != nil {
			return err
		}

		if nj.AlertId == 0 && nj.Id == 0 && nj.NotifierId == 0 && nj.OrgId == 0 {
			return m.ErrJournalingNotFound
		}

		cmd.Result = nj
		return nil
	})
}

func CleanNotificationJournal(ctx context.Context, cmd *m.CleanNotificationJournalCommand) error {
	return inTransactionCtx(ctx, func(sess *DBSession) error {
		sql := "DELETE FROM alert_notification_journal WHERE alert_notification_journal.org_id = ? AND alert_notification_journal.alert_id = ? AND alert_notification_journal.notifier_id = ?"
		_, err := sess.Exec(sql, cmd.OrgId, cmd.AlertId, cmd.NotifierId)
		return err
	})
}
<|endoftext|>


<commit_before>package docker

import (
	"fmt"
	"strings"

	. "github.com/onsi/gomega"

	"github.com/flant/go-containerregistry/pkg/authn"
	"github.com/flant/go-containerregistry/pkg/name"
	"github.com/flant/go-containerregistry/pkg/v1/remote"

	"github.com/flant/werf/pkg/testing/utils"
)

func RegistryRepositoryList(reference string) []string {
	repo, err := name.NewRepository(reference, name.WeakValidation)
	Ω(err).ShouldNot(HaveOccurred(), fmt.Sprintf("parsing repo %q: %v", reference, err))

	tags, err := remote.List(repo, remote.WithAuthFromKeychain(authn.DefaultKeychain))
	if err != nil && strings.Contains(err.Error(), "NAME_UNKNOWN") {
		return []string{}
	}

	Ω(err).ShouldNot(HaveOccurred(), fmt.Sprintf("reading tags for %q: %v", repo, err))
	return tags
}

func LocalDockerRegistryRun() (string, string) {
	containerName := fmt.Sprintf("werf_test_docker_registry-%s", utils.GetRandomString(10))
	imageName := "registry"

	dockerCliRunArgs := []string{
		"-d",
		"-p", ":5000",
		"-e", "REGISTRY_STORAGE_DELETE_ENABLED=true",
		"--name", containerName,
		imageName,
	}
	err := CliRun(dockerCliRunArgs...)
	Ω(err).ShouldNot(HaveOccurred(), "docker run "+strings.Join(dockerCliRunArgs, " "))

	registry := fmt.Sprintf("localhost:%s", ContainerHostPort(containerName, "5000/tcp"))
	registryWithScheme := fmt.Sprintf("http://%s", registry)

	utils.WaitTillHostReadyToRespond(registryWithScheme, utils.DefaultWaitTillHostReadyToRespondMaxAttempts)

	return registry, containerName
}
<commit_msg>[tests] Own docker registry image without host volume<commit_after>package docker

import (
	"fmt"
	"strings"

	. "github.com/onsi/gomega"

	"github.com/flant/go-containerregistry/pkg/authn"
	"github.com/flant/go-containerregistry/pkg/name"
	"github.com/flant/go-containerregistry/pkg/v1/remote"

	"github.com/flant/werf/pkg/testing/utils"
)

func RegistryRepositoryList(reference string) []string {
	repo, err := name.NewRepository(reference, name.WeakValidation)
	Ω(err).ShouldNot(HaveOccurred(), fmt.Sprintf("parsing repo %q: %v", reference, err))

	tags, err := remote.List(repo, remote.WithAuthFromKeychain(authn.DefaultKeychain))
	if err != nil && strings.Contains(err.Error(), "NAME_UNKNOWN") {
		return []string{}
	}

	Ω(err).ShouldNot(HaveOccurred(), fmt.Sprintf("reading tags for %q: %v", repo, err))
	return tags
}

func LocalDockerRegistryRun() (string, string) {
	containerName := fmt.Sprintf("werf_test_docker_registry-%s", utils.GetRandomString(10))
	imageName := "flant/werf-test:registry"

	dockerCliRunArgs := []string{
		"-d",
		"-p", ":5000",
		"-e", "REGISTRY_STORAGE_DELETE_ENABLED=true",
		"--name", containerName,
		imageName,
	}
	err := CliRun(dockerCliRunArgs...)
	Ω(err).ShouldNot(HaveOccurred(), "docker run "+strings.Join(dockerCliRunArgs, " "))

	registry := fmt.Sprintf("localhost:%s", ContainerHostPort(containerName, "5000/tcp"))
	registryWithScheme := fmt.Sprintf("http://%s", registry)

	utils.WaitTillHostReadyToRespond(registryWithScheme, utils.DefaultWaitTillHostReadyToRespondMaxAttempts)

	return registry, containerName
}
<|endoftext|>


<commit_before>package dnsmadeeasy

import (
	"strconv"

	"github.com/StackExchange/dnscontrol/v3/models"
)

type singleDomainResponse struct {
	ID                  int                              `json:"id"`
	Name                string                           `json:"name"`
	DelegateNameServers []string                         `json:"delegateNameServers"`
	NameServers         []singleDomainResponseNameServer `json:"nameServers"`
	ProcessMulti        bool                             `json:"processMulti"`
	ActiveThirdParties  []interface{}                    `json:"activeThirdParties"`
	PendingActionID     int                              `json:"pendingActionId"`
	GtdEnabled          bool                             `json:"gtdEnabled"`
	Created             int64                            `json:"created"`
	Updated             int64                            `json:"updated"`
}

type singleDomainResponseNameServer struct {
	Fqdn string `json:"fqdn"`
	Ipv4 string `json:"ipv4"`
	Ipv6 string `json:"ipv6"`
}

type singleDomainRequestData struct {
	Name string `json:"name"`
}

type multiDomainResponse struct {
	TotalRecords int                            `json:"totalRecords"`
	TotalPages   int                            `json:"totalPages"`
	Data         []multiDomainResponseDataEntry `json:"data"`
	Page         int                            `json:"page"`
}

type multiDomainResponseDataEntry struct {
	ID                 int           `json:"id"`
	Name               string        `json:"name"`
	FolderID           int           `json:"folderId"`
	GtdEnabled         bool          `json:"gtdEnabled"`
	ProcessMulti       bool          `json:"processMulti"`
	ActiveThirdParties []interface{} `json:"activeThirdParties"`
	PendingActionID    int           `json:"pendingActionId"`
	VanityID           int           `json:"vanityId,omitempty"`
	Created            int64         `json:"created"`
	Updated            int64         `json:"updated"`
}

type recordResponse struct {
	TotalRecords int                       `json:"totalRecords"`
	TotalPages   int                       `json:"totalPages"`
	Data         []recordResponseDataEntry `json:"data"`
	Page         int                       `json:"page"`
}

type recordResponseDataEntry struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Type  string `json:"type"`
	Value string `json:"value"`
	TTL   int    `json:"ttl"`

	Source   int `json:"source"`
	SourceID int `json:"sourceId"`

	DynamicDNS bool   `json:"dynamicDns"`
	Password   string `json:"password"`

	// A records
	Monitor  bool `json:"monitor"`
	Failover bool `json:"failover"`
	Failed   bool `json:"failed"`

	// Global Traffic Director
	GtdLocation string `json:"gtdLocation"`

	// HTTPRED records
	Description  string `json:"description"`
	Keywords     string `json:"keywords"`
	Title        string `json:"title"`
	RedirectType string `json:"redirectType"`
	HardLink     bool   `json:"hardLink"`

	// MX records
	MxLevel int `json:"mxLevel"`

	// SRV records
	Weight   int `json:"weight"`
	Priority int `json:"Priority"`
	Port     int `json:"port"`

	// CAA records
	CaaType        string `json:"caaType"`
	IssuerCritical int    `json:"issuerCritical"`
}

type recordRequestData struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Type  string `json:"type"`
	Value string `json:"value"`
	TTL   int    `json:"ttl"`

	// Global Traffic Director
	GtdLocation string `json:"gtdLocation"`

	// MX records
	MxLevel int `json:"mxLevel"`

	// SRV records
	Weight   int `json:"weight,omitempty"`
	Priority int `json:"priority,omitempty"`
	Port     int `json:"port,omitempty"`

	// CAA records
	CaaType        string `json:"caaType"`
	IssuerCritical int    `json:"issuerCritical"`
}

func toRecordConfig(domain string, record *recordResponseDataEntry) *models.RecordConfig {
	rc := &models.RecordConfig{
		Type:     record.Type,
		TTL:      uint32(record.TTL),
		Original: record,
	}

	rc.SetLabel(record.Name, domain)

	var err error
	if record.Type == "MX" {
		err = rc.SetTargetMX(uint16(record.MxLevel), record.Value)
	} else if record.Type == "SRV" {
		err = rc.SetTargetSRV(uint16(record.Priority), uint16(record.Weight), uint16(record.Port), record.Value)
	} else if record.Type == "CAA" {
		value, err := strconv.Unquote(record.Value)
		if err != nil {
			panic(err)
		}
		err = rc.SetTargetCAA(uint8(record.IssuerCritical), record.CaaType, value)
	} else {
		err = rc.PopulateFromString(record.Type, record.Value, domain)
	}

	if err != nil {
		panic(err)
	}

	return rc
}

func fromRecordConfig(rc *models.RecordConfig) *recordRequestData {
	label := rc.GetLabel()
	if label == "@" {
		label = ""
	}

	record := &recordRequestData{
		Type:        rc.Type,
		TTL:         int(rc.TTL),
		GtdLocation: "DEFAULT",
		Name:        label,
		Value:       rc.GetTargetCombined(),
	}

	if record.Type == "MX" {
		record.MxLevel = int(rc.MxPreference)
		record.Value = rc.GetTargetField()
	} else if record.Type == "SRV" {
		target := rc.GetTargetField()
		if target == "." {
			target += "."
		}

		record.Priority = int(rc.SrvPriority)
		record.Weight = int(rc.SrvWeight)
		record.Port = int(rc.SrvPort)
		record.Value = target
	} else if record.Type == "CAA" {
		record.IssuerCritical = int(rc.CaaFlag)
		record.CaaType = rc.CaaTag
		record.Value = rc.GetTargetField()
	}

	return record
}

func systemNameServerToRecordConfig(domain string, nameServer string) *models.RecordConfig {
	target := nameServer + "."
	return toRecordConfig(domain, &recordResponseDataEntry{Type: "NS", Value: target, TTL: int(models.DefaultTTL)})
}
<commit_msg>Correct hard coded DNS Made Easy system nameserver TTL (#1167)<commit_after>package dnsmadeeasy

import (
	"strconv"

	"github.com/StackExchange/dnscontrol/v3/models"
)

type singleDomainResponse struct {
	ID                  int                              `json:"id"`
	Name                string                           `json:"name"`
	DelegateNameServers []string                         `json:"delegateNameServers"`
	NameServers         []singleDomainResponseNameServer `json:"nameServers"`
	ProcessMulti        bool                             `json:"processMulti"`
	ActiveThirdParties  []interface{}                    `json:"activeThirdParties"`
	PendingActionID     int                              `json:"pendingActionId"`
	GtdEnabled          bool                             `json:"gtdEnabled"`
	Created             int64                            `json:"created"`
	Updated             int64                            `json:"updated"`
}

type singleDomainResponseNameServer struct {
	Fqdn string `json:"fqdn"`
	Ipv4 string `json:"ipv4"`
	Ipv6 string `json:"ipv6"`
}

type singleDomainRequestData struct {
	Name string `json:"name"`
}

type multiDomainResponse struct {
	TotalRecords int                            `json:"totalRecords"`
	TotalPages   int                            `json:"totalPages"`
	Data         []multiDomainResponseDataEntry `json:"data"`
	Page         int                            `json:"page"`
}

type multiDomainResponseDataEntry struct {
	ID                 int           `json:"id"`
	Name               string        `json:"name"`
	FolderID           int           `json:"folderId"`
	GtdEnabled         bool          `json:"gtdEnabled"`
	ProcessMulti       bool          `json:"processMulti"`
	ActiveThirdParties []interface{} `json:"activeThirdParties"`
	PendingActionID    int           `json:"pendingActionId"`
	VanityID           int           `json:"vanityId,omitempty"`
	Created            int64         `json:"created"`
	Updated            int64         `json:"updated"`
}

type recordResponse struct {
	TotalRecords int                       `json:"totalRecords"`
	TotalPages   int                       `json:"totalPages"`
	Data         []recordResponseDataEntry `json:"data"`
	Page         int                       `json:"page"`
}

type recordResponseDataEntry struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Type  string `json:"type"`
	Value string `json:"value"`
	TTL   int    `json:"ttl"`

	Source   int `json:"source"`
	SourceID int `json:"sourceId"`

	DynamicDNS bool   `json:"dynamicDns"`
	Password   string `json:"password"`

	// A records
	Monitor  bool `json:"monitor"`
	Failover bool `json:"failover"`
	Failed   bool `json:"failed"`

	// Global Traffic Director
	GtdLocation string `json:"gtdLocation"`

	// HTTPRED records
	Description  string `json:"description"`
	Keywords     string `json:"keywords"`
	Title        string `json:"title"`
	RedirectType string `json:"redirectType"`
	HardLink     bool   `json:"hardLink"`

	// MX records
	MxLevel int `json:"mxLevel"`

	// SRV records
	Weight   int `json:"weight"`
	Priority int `json:"Priority"`
	Port     int `json:"port"`

	// CAA records
	CaaType        string `json:"caaType"`
	IssuerCritical int    `json:"issuerCritical"`
}

type recordRequestData struct {
	ID    int    `json:"id"`
	Name  string `json:"name"`
	Type  string `json:"type"`
	Value string `json:"value"`
	TTL   int    `json:"ttl"`

	// Global Traffic Director
	GtdLocation string `json:"gtdLocation"`

	// MX records
	MxLevel int `json:"mxLevel"`

	// SRV records
	Weight   int `json:"weight,omitempty"`
	Priority int `json:"priority,omitempty"`
	Port     int `json:"port,omitempty"`

	// CAA records
	CaaType        string `json:"caaType"`
	IssuerCritical int    `json:"issuerCritical"`
}

func toRecordConfig(domain string, record *recordResponseDataEntry) *models.RecordConfig {
	rc := &models.RecordConfig{
		Type:     record.Type,
		TTL:      uint32(record.TTL),
		Original: record,
	}

	rc.SetLabel(record.Name, domain)

	var err error
	if record.Type == "MX" {
		err = rc.SetTargetMX(uint16(record.MxLevel), record.Value)
	} else if record.Type == "SRV" {
		err = rc.SetTargetSRV(uint16(record.Priority), uint16(record.Weight), uint16(record.Port), record.Value)
	} else if record.Type == "CAA" {
		value, err := strconv.Unquote(record.Value)
		if err != nil {
			panic(err)
		}
		err = rc.SetTargetCAA(uint8(record.IssuerCritical), record.CaaType, value)
	} else {
		err = rc.PopulateFromString(record.Type, record.Value, domain)
	}

	if err != nil {
		panic(err)
	}

	return rc
}

func fromRecordConfig(rc *models.RecordConfig) *recordRequestData {
	label := rc.GetLabel()
	if label == "@" {
		label = ""
	}

	record := &recordRequestData{
		Type:        rc.Type,
		TTL:         int(rc.TTL),
		GtdLocation: "DEFAULT",
		Name:        label,
		Value:       rc.GetTargetCombined(),
	}

	if record.Type == "MX" {
		record.MxLevel = int(rc.MxPreference)
		record.Value = rc.GetTargetField()
	} else if record.Type == "SRV" {
		target := rc.GetTargetField()
		if target == "." {
			target += "."
		}

		record.Priority = int(rc.SrvPriority)
		record.Weight = int(rc.SrvWeight)
		record.Port = int(rc.SrvPort)
		record.Value = target
	} else if record.Type == "CAA" {
		record.IssuerCritical = int(rc.CaaFlag)
		record.CaaType = rc.CaaTag
		record.Value = rc.GetTargetField()
	}

	return record
}

func systemNameServerToRecordConfig(domain string, nameServer string) *models.RecordConfig {
	// DNS Made Easy does not allow the system name servers to be edited, and said records appear to always have a fixed TTL of 86400.
	const fixedNameServerRecordTTL = 86400;

	target := nameServer + "."
	return toRecordConfig(domain, &recordResponseDataEntry{Type: "NS", Value: target, TTL: fixedNameServerRecordTTL})
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 l3plugin

import (
	"fmt"
	"net"
	"time"

	"github.com/ligato/cn-infra/logging/measure"
	"github.com/ligato/vpp-agent/plugins/linuxplugin/common/model/l3"
	"github.com/ligato/vpp-agent/plugins/linuxplugin/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/linuxplugin/ifplugin/linuxcalls"
	l3linuxcalls "github.com/ligato/vpp-agent/plugins/linuxplugin/l3plugin/linuxcalls"
	"github.com/vishvananda/netlink"
)

// Resync configures an initial set of ARPs. Existing Linux ARPs are registered and potentially re-configured.
func (plugin *LinuxArpConfigurator) Resync(arpEntries []*l3.LinuxStaticArpEntries_ArpEntry) (errs []error) {
	plugin.Log.WithField("cfg", plugin).Debug("RESYNC ARPs begin.")

	start := time.Now()
	defer func() {
		if plugin.Stopwatch != nil {
			timeLog := measure.GetTimeLog("linux-arp resync", plugin.Stopwatch)
			timeLog.LogTimeEntry(time.Since(start))
		}
	}()

	// Create missing arp entries and update existing ones
	for _, entry := range arpEntries {
		err := plugin.ConfigureLinuxStaticArpEntry(entry)
		if err != nil {
			errs = append(errs, err)
		}
	}

	// Dump pre-existing not managed arp entries
	err := plugin.LookupLinuxArpEntries()
	if err != nil {
		errs = append(errs, err)
	}

	plugin.Log.WithField("cfg", plugin).Debug("RESYNC ARPs end. ")

	return
}

// Resync configures an initial set of static routes. Existing Linux static routes are registered and potentially
// re-configured. Resync does not remove any linux route.
func (plugin *LinuxRouteConfigurator) Resync(nbRoutes []*l3.LinuxStaticRoutes_Route) (errs []error) {
	plugin.Log.WithField("cfg", plugin).Debug("RESYNC static routes begin.")

	start := time.Now()
	defer func() {
		if plugin.Stopwatch != nil {
			timeLog := measure.GetTimeLog("linux-route resync", plugin.Stopwatch)
			timeLog.LogTimeEntry(time.Since(start))
		}
	}()

	nsMgmtCtx := linuxcalls.NewNamespaceMgmtCtx()

	// First step is to find a linux equivalent for NB route config
	for _, nbRoute := range nbRoutes {
		// Route interface exists
		if nbRoute.Interface != "" {
			_, _, found := plugin.LinuxIfIdx.LookupIdx(nbRoute.Interface)
			if !found {
				// If route interface does not exist, cache it
				plugin.Log.Debugf("RESYNC static route %v: interface %s does not exists, moving to cache",
					nbRoute.Name, nbRoute.Interface)
				plugin.rtCachedIfRoutes.RegisterName(nbRoute.Name, plugin.RouteIdxSeq, nbRoute)
				plugin.RouteIdxSeq++
				continue
			}
		}

		// There can be several routes found according to matching parameters
		linuxRtList, err := plugin.findLinuxRoutes(nbRoute, nsMgmtCtx)
		if err != nil {
			plugin.Log.Error(err)
			errs = append(errs, err)
			continue
		}
		plugin.Log.Debugf("found %d linux routes to compare for %s", len(linuxRtList), nbRoute.Name)
		// Find at least one route which has the same parameters
		var rtFound bool
		for rtIdx, linuxRtEntry := range linuxRtList {
			// Route interface interface
			var hostName string
			var ifData *ifaceidx.IndexedLinuxInterface
			if linuxRtEntry.LinkIndex != 0 {
				var found bool
				var nsName string
				if nbRoute.Namespace == nil {
					nsName = ifaceidx.DefNs
				} else {
					nsName = nbRoute.Namespace.Name
				}
				_, ifData, found = plugin.LinuxIfIdx.LookupNameByNamespace(uint32(linuxRtEntry.LinkIndex), nsName)
				if !found || ifData == nil {
					plugin.Log.Debugf("Interface %d (data %v) not found for route", linuxRtEntry.LinkIndex, ifData)
				} else {
					hostName = ifData.Data.HostIfName
				}
			}
			linuxRt := plugin.transformRoute(linuxRtEntry, hostName)
			if plugin.isRouteEqual(rtIdx, nbRoute, linuxRt) {
				rtFound = true
				break
			}
		}
		if rtFound {
			// Register route if found
			plugin.Log.Debugf("RESYNC Linux routes: %s was found and will be registered without additional changes", nbRoute.Name)
			plugin.rtIndexes.RegisterName(nbRoute.Name, plugin.RouteIdxSeq, nbRoute)
			plugin.RouteIdxSeq++
			// Resolve cached routes
			if !nbRoute.Default {
				plugin.retryDefaultRoutes(nbRoute)
			}
		} else {
			// Configure route if not found
			plugin.Log.Debugf("RESYNC Linux routes: %s was not found and will be configured", nbRoute.Name)
			if err := plugin.ConfigureLinuxStaticRoute(nbRoute); err != nil {
				plugin.Log.Error(err)
				errs = append(errs, err)
			}
		}
	}

	return
}

// Look for routes similar to provided NB config in respective namespace. Routes can be read using destination address
// or interface. FOr every config, both ways are used.
func (plugin *LinuxRouteConfigurator) findLinuxRoutes(nbRoute *l3.LinuxStaticRoutes_Route, nsMgmtCtx *linuxcalls.NamespaceMgmtCtx) ([]netlink.Route, error) {
	plugin.Log.Debugf("Looking for equivalent linux routes for %s", nbRoute.Name)

	// Move to proper namespace
	if nbRoute.Namespace != nil {
		// Switch to namespace
		routeNs := l3linuxcalls.ToGenericRouteNs(nbRoute.Namespace)
		revertNs, err := routeNs.SwitchNamespace(nsMgmtCtx, plugin.Log)
		if err != nil {
			return nil, fmt.Errorf("RESYNC Linux route %s: failed to switch to namespace %s: %v",
				nbRoute.Name, nbRoute.Namespace.Name, err)
		}
		defer revertNs()
	}
	var linuxRoutes []netlink.Route
	// Look for routes using destination IP address
	if nbRoute.DstIpAddr != "" {
		_, dstNetIP, err := net.ParseCIDR(nbRoute.DstIpAddr)
		if err != nil {
			return nil, fmt.Errorf("failed to parse destination IP address %s: %v", nbRoute.DstIpAddr, err)
		}
		linuxRts, err := netlink.RouteGet(dstNetIP.IP)
		if err != nil {
			return nil, fmt.Errorf("failed to read linux route %s using address %s: %v",
				nbRoute.Name, nbRoute.DstIpAddr, err)
		}
		if linuxRts != nil {
			linuxRoutes = append(linuxRoutes, linuxRts...)
		}
	}
	// Look for routes using interface
	if nbRoute.Interface != "" {
		// Look whether interface is registered
		_, meta, found := plugin.LinuxIfIdx.LookupIdx(nbRoute.Interface)
		if !found {
			// Should not happen, was successfully checked before
			plugin.Log.Errorf("Route %s interface %s is missing from the mapping", nbRoute.Name, nbRoute.Interface)
		} else if meta == nil || meta.Data == nil {
			plugin.Log.Errorf("Interface %s data missing", nbRoute.Interface)
		} else {
			// Look for interface using host name
			link, err := netlink.LinkByName(meta.Data.HostIfName)
			if err != nil {
				return nil, fmt.Errorf("failed to read interface %s: %v", meta.Data.HostIfName, err)
			}
			linuxRts, err := netlink.RouteList(link, netlink.FAMILY_ALL)
			if err != nil {
				return nil, fmt.Errorf("failed to read linux route %s using interface %s: %v",
					nbRoute.Name, meta.Data.HostIfName, err)
			}
			if linuxRts != nil {
				linuxRoutes = append(linuxRoutes, linuxRts...)
			}
		}
	}

	if len(linuxRoutes) == 0 {
		plugin.Log.Debugf("Equivalent for route %s was not found", nbRoute.Name)
	}

	return linuxRoutes, nil
}

// Compare all route parameters and returns true if routes are equal, false otherwise
func (plugin *LinuxRouteConfigurator) isRouteEqual(rtIdx int, nbRoute, linuxRt *l3.LinuxStaticRoutes_Route) bool {
	// Interface (if exists)
	if nbRoute.Interface != "" && nbRoute.Interface != linuxRt.Interface {
		plugin.Log.Debugf("Linux route %d: interface is different (NB: %s, Linux: %s)",
			rtIdx, nbRoute.Interface, linuxRt.Interface)
		return false
	}
	// Default route
	if nbRoute.Default {
		if !linuxRt.Default {
			plugin.Log.Debugf("Linux route %d: NB route is default, but linux route is not", rtIdx)
			return false
		}
		if nbRoute.GwAddr != linuxRt.GwAddr {
			plugin.Log.Debugf("Linux route %d: gateway is different (NB: %s, Linux: %s)",
				rtIdx, nbRoute.GwAddr, linuxRt.GwAddr)
			return false
		}
		if nbRoute.Metric != linuxRt.Metric {
			plugin.Log.Debugf("Linux route %d: metric is different (NB: %s, Linux: %s)",
				rtIdx, nbRoute.Metric, linuxRt.Metric)
			return false
		}
		return true
	}
	// Static route
	_, nbIPNet, err := net.ParseCIDR(nbRoute.DstIpAddr)
	if err != nil {
		plugin.Log.Error(err)
		return false
	}
	if nbIPNet.IP.String() != linuxRt.DstIpAddr {
		plugin.Log.Debugf("Linux route %d: destination address is different (NB: %s, Linux: %s)",
			rtIdx, nbIPNet.IP.String(), linuxRt.DstIpAddr)
		return false
	}
	// Compare source IP/gateway
	if nbRoute.SrcIpAddr == "" && linuxRt.SrcIpAddr != "" || nbRoute.SrcIpAddr != "" && linuxRt.SrcIpAddr == "" {
		if nbRoute.SrcIpAddr == "" && nbRoute.SrcIpAddr != linuxRt.GwAddr {
			plugin.Log.Debugf("Linux route %d: source does not match gateway (NB: %s, Linux: %s)",
				rtIdx, nbRoute.SrcIpAddr, linuxRt.SrcIpAddr)
			return false
		} else if linuxRt.SrcIpAddr == "" && nbRoute.GwAddr != linuxRt.SrcIpAddr {
			plugin.Log.Debugf("Linux route %d: source does not match gateway (NB: %s, Linux: %s)",
				rtIdx, nbRoute.SrcIpAddr, linuxRt.SrcIpAddr)
			return false
		}
	} else {
		plugin.Log.Debugf("Linux route %d: source address is different (NB: %s, Linux: %s)",
			rtIdx, nbRoute.SrcIpAddr, linuxRt.SrcIpAddr)
		return false
	}

	if nbRoute.SrcIpAddr != "" && nbRoute.SrcIpAddr != linuxRt.SrcIpAddr {
		plugin.Log.Debugf("Linux route %d: source address is different (NB: %s, Linux: %s)",
			rtIdx, nbRoute.SrcIpAddr, linuxRt.SrcIpAddr)
		return false
	}
	// If NB scope is nil, set scope type LINK (default value)
	if nbRoute.Scope == nil {
		nbRoute.Scope = &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_LINK,
		}
	} else if linuxRt.Scope != nil {
		if nbRoute.Scope.Type != linuxRt.Scope.Type {
			plugin.Log.Debugf("Linux route %d: scope is different (NB: %s, Linux: %s)",
				rtIdx, nbRoute.Scope.Type, linuxRt.Scope.Type)
			return false
		}
	}

	return true
}

// Parse netlink type scope to proto
func (plugin *LinuxRouteConfigurator) parseLinuxRouteScope(scope netlink.Scope) *l3.LinuxStaticRoutes_Route_Scope {
	switch scope {
	case netlink.SCOPE_UNIVERSE:
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_GLOBAL,
		}
	case netlink.SCOPE_HOST:
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_HOST,
		}
	case netlink.SCOPE_LINK:
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_LINK,
		}
	case netlink.SCOPE_SITE:
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_SITE,
		}
	default:
		plugin.Log.Infof("Unknown scope type, setting to default (link): %v", scope)
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_LINK,
		}
	}
}
<commit_msg>added network reachability check while reading routes with dst address<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 l3plugin

import (
	"fmt"
	"net"
	"time"

	"github.com/ligato/cn-infra/logging/measure"
	"github.com/ligato/vpp-agent/plugins/linuxplugin/common/model/l3"
	"github.com/ligato/vpp-agent/plugins/linuxplugin/ifplugin/ifaceidx"
	"github.com/ligato/vpp-agent/plugins/linuxplugin/ifplugin/linuxcalls"
	l3linuxcalls "github.com/ligato/vpp-agent/plugins/linuxplugin/l3plugin/linuxcalls"
	"github.com/vishvananda/netlink"
)

// Resync configures an initial set of ARPs. Existing Linux ARPs are registered and potentially re-configured.
func (plugin *LinuxArpConfigurator) Resync(arpEntries []*l3.LinuxStaticArpEntries_ArpEntry) (errs []error) {
	plugin.Log.WithField("cfg", plugin).Debug("RESYNC ARPs begin.")

	start := time.Now()
	defer func() {
		if plugin.Stopwatch != nil {
			timeLog := measure.GetTimeLog("linux-arp resync", plugin.Stopwatch)
			timeLog.LogTimeEntry(time.Since(start))
		}
	}()

	// Create missing arp entries and update existing ones
	for _, entry := range arpEntries {
		err := plugin.ConfigureLinuxStaticArpEntry(entry)
		if err != nil {
			errs = append(errs, err)
		}
	}

	// Dump pre-existing not managed arp entries
	err := plugin.LookupLinuxArpEntries()
	if err != nil {
		errs = append(errs, err)
	}

	plugin.Log.WithField("cfg", plugin).Debug("RESYNC ARPs end. ")

	return
}

// Resync configures an initial set of static routes. Existing Linux static routes are registered and potentially
// re-configured. Resync does not remove any linux route.
func (plugin *LinuxRouteConfigurator) Resync(nbRoutes []*l3.LinuxStaticRoutes_Route) (errs []error) {
	plugin.Log.WithField("cfg", plugin).Debug("RESYNC static routes begin.")

	start := time.Now()
	defer func() {
		if plugin.Stopwatch != nil {
			timeLog := measure.GetTimeLog("linux-route resync", plugin.Stopwatch)
			timeLog.LogTimeEntry(time.Since(start))
		}
	}()

	nsMgmtCtx := linuxcalls.NewNamespaceMgmtCtx()

	// First step is to find a linux equivalent for NB route config
	for _, nbRoute := range nbRoutes {
		// Route interface exists
		if nbRoute.Interface != "" {
			_, _, found := plugin.LinuxIfIdx.LookupIdx(nbRoute.Interface)
			if !found {
				// If route interface does not exist, cache it
				plugin.Log.Debugf("RESYNC static route %v: interface %s does not exists, moving to cache",
					nbRoute.Name, nbRoute.Interface)
				plugin.rtCachedIfRoutes.RegisterName(nbRoute.Name, plugin.RouteIdxSeq, nbRoute)
				plugin.RouteIdxSeq++
				continue
			}
		}

		// There can be several routes found according to matching parameters
		linuxRtList, err := plugin.findLinuxRoutes(nbRoute, nsMgmtCtx)
		if err != nil {
			plugin.Log.Error(err)
			errs = append(errs, err)
			continue
		}
		plugin.Log.Debugf("found %d linux routes to compare for %s", len(linuxRtList), nbRoute.Name)
		// Find at least one route which has the same parameters
		var rtFound bool
		for rtIdx, linuxRtEntry := range linuxRtList {
			// Route interface interface
			var hostName string
			var ifData *ifaceidx.IndexedLinuxInterface
			if linuxRtEntry.LinkIndex != 0 {
				var found bool
				var nsName string
				if nbRoute.Namespace == nil {
					nsName = ifaceidx.DefNs
				} else {
					nsName = nbRoute.Namespace.Name
				}
				_, ifData, found = plugin.LinuxIfIdx.LookupNameByNamespace(uint32(linuxRtEntry.LinkIndex), nsName)
				if !found || ifData == nil {
					plugin.Log.Debugf("Interface %d (data %v) not found for route", linuxRtEntry.LinkIndex, ifData)
				} else {
					hostName = ifData.Data.HostIfName
				}
			}
			linuxRt := plugin.transformRoute(linuxRtEntry, hostName)
			if plugin.isRouteEqual(rtIdx, nbRoute, linuxRt) {
				rtFound = true
				break
			}
		}
		if rtFound {
			// Register route if found
			plugin.Log.Debugf("RESYNC Linux routes: %s was found and will be registered without additional changes", nbRoute.Name)
			plugin.rtIndexes.RegisterName(nbRoute.Name, plugin.RouteIdxSeq, nbRoute)
			plugin.RouteIdxSeq++
			// Resolve cached routes
			if !nbRoute.Default {
				plugin.retryDefaultRoutes(nbRoute)
			}
		} else {
			// Configure route if not found
			plugin.Log.Debugf("RESYNC Linux routes: %s was not found and will be configured", nbRoute.Name)
			if err := plugin.ConfigureLinuxStaticRoute(nbRoute); err != nil {
				plugin.Log.Error(err)
				errs = append(errs, err)
			}
		}
	}

	return
}

// Look for routes similar to provided NB config in respective namespace. Routes can be read using destination address
// or interface. FOr every config, both ways are used.
func (plugin *LinuxRouteConfigurator) findLinuxRoutes(nbRoute *l3.LinuxStaticRoutes_Route, nsMgmtCtx *linuxcalls.NamespaceMgmtCtx) ([]netlink.Route, error) {
	plugin.Log.Debugf("Looking for equivalent linux routes for %s", nbRoute.Name)

	// Move to proper namespace
	if nbRoute.Namespace != nil {
		// Switch to namespace
		routeNs := l3linuxcalls.ToGenericRouteNs(nbRoute.Namespace)
		revertNs, err := routeNs.SwitchNamespace(nsMgmtCtx, plugin.Log)
		if err != nil {
			return nil, fmt.Errorf("RESYNC Linux route %s: failed to switch to namespace %s: %v",
				nbRoute.Name, nbRoute.Namespace.Name, err)
		}
		defer revertNs()
	}
	var linuxRoutes []netlink.Route
	// Look for routes using destination IP address
	if nbRoute.DstIpAddr != "" && plugin.networkReachable(nbRoute.Namespace, nbRoute.DstIpAddr) {
		_, dstNetIP, err := net.ParseCIDR(nbRoute.DstIpAddr)
		if err != nil {
			return nil, fmt.Errorf("failed to parse destination IP address %s: %v", nbRoute.DstIpAddr, err)
		}
		linuxRts, err := netlink.RouteGet(dstNetIP.IP)
		if err != nil {
			return nil, fmt.Errorf("failed to read linux route %s using address %s: %v",
				nbRoute.Name, nbRoute.DstIpAddr, err)
		}
		if linuxRts != nil {
			linuxRoutes = append(linuxRoutes, linuxRts...)
		}
	}
	// Look for routes using interface
	if nbRoute.Interface != "" {
		// Look whether interface is registered
		_, meta, found := plugin.LinuxIfIdx.LookupIdx(nbRoute.Interface)
		if !found {
			// Should not happen, was successfully checked before
			plugin.Log.Errorf("Route %s interface %s is missing from the mapping", nbRoute.Name, nbRoute.Interface)
		} else if meta == nil || meta.Data == nil {
			plugin.Log.Errorf("Interface %s data missing", nbRoute.Interface)
		} else {
			// Look for interface using host name
			link, err := netlink.LinkByName(meta.Data.HostIfName)
			if err != nil {
				return nil, fmt.Errorf("failed to read interface %s: %v", meta.Data.HostIfName, err)
			}
			linuxRts, err := netlink.RouteList(link, netlink.FAMILY_ALL)
			if err != nil {
				return nil, fmt.Errorf("failed to read linux route %s using interface %s: %v",
					nbRoute.Name, meta.Data.HostIfName, err)
			}
			if linuxRts != nil {
				linuxRoutes = append(linuxRoutes, linuxRts...)
			}
		}
	}

	if len(linuxRoutes) == 0 {
		plugin.Log.Debugf("Equivalent for route %s was not found", nbRoute.Name)
	}

	return linuxRoutes, nil
}

// Compare all route parameters and returns true if routes are equal, false otherwise
func (plugin *LinuxRouteConfigurator) isRouteEqual(rtIdx int, nbRoute, linuxRt *l3.LinuxStaticRoutes_Route) bool {
	// Interface (if exists)
	if nbRoute.Interface != "" && nbRoute.Interface != linuxRt.Interface {
		plugin.Log.Debugf("Linux route %d: interface is different (NB: %s, Linux: %s)",
			rtIdx, nbRoute.Interface, linuxRt.Interface)
		return false
	}
	// Default route
	if nbRoute.Default {
		if !linuxRt.Default {
			plugin.Log.Debugf("Linux route %d: NB route is default, but linux route is not", rtIdx)
			return false
		}
		if nbRoute.GwAddr != linuxRt.GwAddr {
			plugin.Log.Debugf("Linux route %d: gateway is different (NB: %s, Linux: %s)",
				rtIdx, nbRoute.GwAddr, linuxRt.GwAddr)
			return false
		}
		if nbRoute.Metric != linuxRt.Metric {
			plugin.Log.Debugf("Linux route %d: metric is different (NB: %s, Linux: %s)",
				rtIdx, nbRoute.Metric, linuxRt.Metric)
			return false
		}
		return true
	}
	// Static route
	_, nbIPNet, err := net.ParseCIDR(nbRoute.DstIpAddr)
	if err != nil {
		plugin.Log.Error(err)
		return false
	}
	if nbIPNet.IP.String() != linuxRt.DstIpAddr {
		plugin.Log.Debugf("Linux route %d: destination address is different (NB: %s, Linux: %s)",
			rtIdx, nbIPNet.IP.String(), linuxRt.DstIpAddr)
		return false
	}
	// Compare source IP/gateway
	if nbRoute.SrcIpAddr == "" && linuxRt.SrcIpAddr != "" || nbRoute.SrcIpAddr != "" && linuxRt.SrcIpAddr == "" {
		if nbRoute.SrcIpAddr == "" && nbRoute.SrcIpAddr != linuxRt.GwAddr {
			plugin.Log.Debugf("Linux route %d: source does not match gateway (NB: %s, Linux: %s)",
				rtIdx, nbRoute.SrcIpAddr, linuxRt.SrcIpAddr)
			return false
		} else if linuxRt.SrcIpAddr == "" && nbRoute.GwAddr != linuxRt.SrcIpAddr {
			plugin.Log.Debugf("Linux route %d: source does not match gateway (NB: %s, Linux: %s)",
				rtIdx, nbRoute.SrcIpAddr, linuxRt.SrcIpAddr)
			return false
		}
	} else if nbRoute.SrcIpAddr != "" && linuxRt.SrcIpAddr != "" && nbRoute.SrcIpAddr != linuxRt.SrcIpAddr {
		plugin.Log.Debugf("Linux route %d: source address is different (NB: %s, Linux: %s)",
			rtIdx, nbRoute.SrcIpAddr, linuxRt.SrcIpAddr)
		return false
	}

	if nbRoute.SrcIpAddr != "" && nbRoute.SrcIpAddr != linuxRt.SrcIpAddr {
		plugin.Log.Debugf("Linux route %d: source address is different (NB: %s, Linux: %s)",
			rtIdx, nbRoute.SrcIpAddr, linuxRt.SrcIpAddr)
		return false
	}
	// If NB scope is nil, set scope type LINK (default value)
	if nbRoute.Scope == nil {
		nbRoute.Scope = &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_LINK,
		}
	} else if linuxRt.Scope != nil {
		if nbRoute.Scope.Type != linuxRt.Scope.Type {
			plugin.Log.Debugf("Linux route %d: scope is different (NB: %s, Linux: %s)",
				rtIdx, nbRoute.Scope.Type, linuxRt.Scope.Type)
			return false
		}
	}

	return true
}

// Parse netlink type scope to proto
func (plugin *LinuxRouteConfigurator) parseLinuxRouteScope(scope netlink.Scope) *l3.LinuxStaticRoutes_Route_Scope {
	switch scope {
	case netlink.SCOPE_UNIVERSE:
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_GLOBAL,
		}
	case netlink.SCOPE_HOST:
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_HOST,
		}
	case netlink.SCOPE_LINK:
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_LINK,
		}
	case netlink.SCOPE_SITE:
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_SITE,
		}
	default:
		plugin.Log.Infof("Unknown scope type, setting to default (link): %v", scope)
		return &l3.LinuxStaticRoutes_Route_Scope{
			Type: l3.LinuxStaticRoutes_Route_Scope_LINK,
		}
	}
}
<|endoftext|>


<commit_before>/*
   Hockeypuck - OpenPGP key server
   Copyright (C) 2012  Casey Marshall

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, version 3.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Package jsonhkp defines an arbitrary, Hockeypuck-specific, JSON-friendly
// document model for representation of OpenPGP key material. Intended to be
// used by front-end Javascript as well as server-side HTML template developers.
package jsonhkp

import (
	"encoding/base64"
	"fmt"
	"io"
	"net/url"
	"time"

	"gopkg.in/errgo.v1"

	"gopkg.in/hockeypuck/openpgp.v1"
)

type Packet struct {
	Tag    uint8  `json:"tag"`
	Data   []byte `json:"data"`
	Parsed bool   `json:"parsed"`
}

func NewPacket(from *openpgp.Packet) *Packet {
	return &Packet{
		Tag:    from.Tag,
		Data:   from.Packet,
		Parsed: from.Parsed,
	}
}

type algorithm struct {
	Name string `json:"name"`
	Code int    `json:"code"`
}

type publicKey struct {
	Fingerprint  string       `json:"fingerprint"`
	LongKeyID    string       `json:"longKeyID"`
	ShortKeyID   string       `json:"shortKeyID"`
	Creation     string       `json:"creation,omitempty"`
	Expiration   string       `json:"expiration,omitempty"`
	NeverExpires bool         `json:"neverExpires,omitempty"`
	Algorithm    algorithm    `json:"algorithm"`
	BitLength    int          `json:"bitLength"`
	Signatures   []*Signature `json:"signatures,omitempty"`
	Unsupported  []*Packet    `json:"unsupported,omitempty"`
	Packet       *Packet      `json:"packet,omitempty"`
}

func newPublicKey(from *openpgp.PublicKey) *publicKey {
	to := &publicKey{
		Fingerprint: from.Fingerprint(),
		LongKeyID:   from.KeyID(),
		ShortKeyID:  from.ShortID(),
		Algorithm: algorithm{
			Name: openpgp.AlgorithmName(from.Algorithm),
			Code: from.Algorithm,
		},
		BitLength: from.BitLen,
		Packet:    NewPacket(&from.Packet),
	}

	if !from.Creation.IsZero() {
		// can happen if openpgp.v1 isn't able to parse this type of key
		to.Creation = from.Creation.UTC().Format(time.RFC3339)
	}

	if !from.Expiration.IsZero() {
		to.Expiration = from.Expiration.UTC().Format(time.RFC3339)
	} else {
		to.NeverExpires = true
	}

	for _, fromSig := range from.Signatures {
		to.Signatures = append(to.Signatures, NewSignature(fromSig))
	}
	for _, fromPkt := range from.Others {
		to.Unsupported = append(to.Unsupported, NewPacket(fromPkt))
	}

	return to
}

type PrimaryKey struct {
	*publicKey

	MD5       string           `json:"md5"`
	SHA256    string           `json:"sha256,omitempty"`
	SubKeys   []*SubKey        `json:"subKeys,omitempty"`
	UserIDs   []*UserID        `json:"userIDs,omitempty"`
	UserAttrs []*UserAttribute `json:"userAttrs,omitempty"`
}

func NewPrimaryKeys(froms []*openpgp.PrimaryKey) []*PrimaryKey {
	var result []*PrimaryKey
	for _, from := range froms {
		result = append(result, NewPrimaryKey(from))
	}
	return result
}

func NewPrimaryKey(from *openpgp.PrimaryKey) *PrimaryKey {
	to := &PrimaryKey{
		publicKey: newPublicKey(&from.PublicKey),
		MD5:       from.MD5,
		SHA256:    from.SHA256,
	}
	for _, fromSubKey := range from.SubKeys {
		to.SubKeys = append(to.SubKeys, NewSubKey(fromSubKey))
	}
	for _, fromUid := range from.UserIDs {
		to.UserIDs = append(to.UserIDs, NewUserID(fromUid))
	}
	for _, fromUat := range from.UserAttributes {
		to.UserAttrs = append(to.UserAttrs, NewUserAttribute(fromUat))
	}
	return to
}

func (pk *PrimaryKey) Serialize(w io.Writer) error {
	packets := pk.packets()
	for _, packet := range packets {
		_, err := w.Write(packet.Data)
		if err != nil {
			return errgo.Mask(err)
		}
	}
	return nil
}

type SubKey struct {
	*publicKey
}

func NewSubKey(from *openpgp.SubKey) *SubKey {
	return &SubKey{
		newPublicKey(&from.PublicKey),
	}
}

type UserID struct {
	Keywords    string       `json:"keywords"`
	Packet      *Packet      `json:"packet,omitempty"`
	Signatures  []*Signature `json:"signatures,omitempty"`
	Unsupported []*Packet    `json:"unsupported,omitempty"`
}

func NewUserID(from *openpgp.UserID) *UserID {
	to := &UserID{
		Keywords: from.Keywords,
		Packet:   NewPacket(&from.Packet),
	}
	for _, fromSig := range from.Signatures {
		to.Signatures = append(to.Signatures, NewSignature(fromSig))
	}
	for _, fromPkt := range from.Others {
		to.Unsupported = append(to.Unsupported, NewPacket(fromPkt))
	}
	return to
}

type UserAttribute struct {
	Photos      []*Photo     `json:"photos,omitempty"`
	Packet      *Packet      `json:"packet,omitempty"`
	Signatures  []*Signature `json:"signatures,omitempty"`
	Unsupported []*Packet    `json:"unsupported,omitempty"`
}

func NewUserAttribute(from *openpgp.UserAttribute) *UserAttribute {
	to := &UserAttribute{
		Packet: NewPacket(&from.Packet),
	}
	for _, image := range from.Images {
		to.Photos = append(to.Photos, NewPhoto(image))
	}
	for _, fromSig := range from.Signatures {
		to.Signatures = append(to.Signatures, NewSignature(fromSig))
	}
	for _, fromPkt := range from.Others {
		to.Unsupported = append(to.Unsupported, NewPacket(fromPkt))
	}
	return to
}

type Photo struct {
	MIMEType string `json:"mimeType"`
	Contents []byte `json:"contents"`
}

func NewPhoto(image []byte) *Photo {
	return &Photo{
		MIMEType: "image/jpeg", // The only image format currently supported, AFAIK
		Contents: image,
	}
}

func (p *Photo) DataURI() (*url.URL, error) {
	return url.Parse(fmt.Sprintf(
		"data:%s;base64,%s", p.MIMEType, base64.StdEncoding.EncodeToString(p.Contents)))
}

type Signature struct {
	SigType      int     `json:"sigType"`
	Revocation   bool    `json:"revocation,omitempty"`
	Primary      bool    `json:"primary,omitempty"`
	IssuerKeyID  string  `json:"issuerKeyID,omitempty"`
	Creation     string  `json:"creation,omitempty"`
	Expiration   string  `json:"expiration,omitempty"`
	NeverExpires bool    `json:"neverExpires,omitempty"`
	Packet       *Packet `json:"packet,omitempty"`
}

func NewSignature(from *openpgp.Signature) *Signature {
	to := &Signature{
		Packet:      NewPacket(&from.Packet),
		SigType:     from.SigType,
		IssuerKeyID: from.IssuerKeyID(),
		Primary:     from.Primary,
	}

	switch to.SigType {
	case 0x20, 0x28, 0x30:
		to.Revocation = true
	}

	if !from.Creation.IsZero() {
		// can happen if openpgp.v1 isn't able to parse this type of signature
		to.Creation = from.Creation.UTC().Format(time.RFC3339)
	}

	if !from.Expiration.IsZero() {
		to.Expiration = from.Expiration.UTC().Format(time.RFC3339)
	} else {
		to.NeverExpires = true
	}

	return to
}

func (pk *PrimaryKey) Bytes() []byte {
	var buf []byte
	for _, pkt := range pk.packets() {
		buf = append(buf, pkt.Data...)
	}
	return buf
}

func (s *Signature) packets() []*Packet {
	packets := []*Packet{s.Packet}
	return packets
}

func (pk *publicKey) packets() []*Packet {
	packets := []*Packet{pk.Packet}
	for _, s := range pk.Signatures {
		packets = append(packets, s.packets()...)
	}
	for _, un := range pk.Unsupported {
		packets = append(packets, un)
	}
	return packets
}

func (u *UserID) packets() []*Packet {
	packets := []*Packet{u.Packet}
	for _, s := range u.Signatures {
		packets = append(packets, s.packets()...)
	}
	for _, un := range u.Unsupported {
		packets = append(packets, un)
	}
	return packets
}

func (u *UserAttribute) packets() []*Packet {
	packets := []*Packet{u.Packet}
	for _, s := range u.Signatures {
		packets = append(packets, s.packets()...)
	}
	for _, un := range u.Unsupported {
		packets = append(packets, un)
	}
	return packets
}

func (pk *PrimaryKey) packets() []*Packet {
	packets := pk.publicKey.packets()
	for _, u := range pk.UserIDs {
		packets = append(packets, u.packets()...)
	}
	for _, u := range pk.UserAttrs {
		packets = append(packets, u.packets()...)
	}
	for _, s := range pk.SubKeys {
		packets = append(packets, s.packets()...)
	}
	return packets
}
<commit_msg>jsonhkp.PublicKey struct needs to be exported<commit_after>/*
   Hockeypuck - OpenPGP key server
   Copyright (C) 2012  Casey Marshall

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, version 3.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Package jsonhkp defines an arbitrary, Hockeypuck-specific, JSON-friendly
// document model for representation of OpenPGP key material. Intended to be
// used by front-end Javascript as well as server-side HTML template developers.
package jsonhkp

import (
	"encoding/base64"
	"fmt"
	"io"
	"net/url"
	"time"

	"gopkg.in/errgo.v1"

	"gopkg.in/hockeypuck/openpgp.v1"
)

type Packet struct {
	Tag    uint8  `json:"tag"`
	Data   []byte `json:"data"`
	Parsed bool   `json:"parsed"`
}

func NewPacket(from *openpgp.Packet) *Packet {
	return &Packet{
		Tag:    from.Tag,
		Data:   from.Packet,
		Parsed: from.Parsed,
	}
}

type algorithm struct {
	Name string `json:"name"`
	Code int    `json:"code"`
}

type PublicKey struct {
	Fingerprint  string       `json:"fingerprint"`
	LongKeyID    string       `json:"longKeyID"`
	ShortKeyID   string       `json:"shortKeyID"`
	Creation     string       `json:"creation,omitempty"`
	Expiration   string       `json:"expiration,omitempty"`
	NeverExpires bool         `json:"neverExpires,omitempty"`
	Algorithm    algorithm    `json:"algorithm"`
	BitLength    int          `json:"bitLength"`
	Signatures   []*Signature `json:"signatures,omitempty"`
	Unsupported  []*Packet    `json:"unsupported,omitempty"`
	Packet       *Packet      `json:"packet,omitempty"`
}

func newPublicKey(from *openpgp.PublicKey) *PublicKey {
	to := &PublicKey{
		Fingerprint: from.Fingerprint(),
		LongKeyID:   from.KeyID(),
		ShortKeyID:  from.ShortID(),
		Algorithm: algorithm{
			Name: openpgp.AlgorithmName(from.Algorithm),
			Code: from.Algorithm,
		},
		BitLength: from.BitLen,
		Packet:    NewPacket(&from.Packet),
	}

	if !from.Creation.IsZero() {
		// can happen if openpgp.v1 isn't able to parse this type of key
		to.Creation = from.Creation.UTC().Format(time.RFC3339)
	}

	if !from.Expiration.IsZero() {
		to.Expiration = from.Expiration.UTC().Format(time.RFC3339)
	} else {
		to.NeverExpires = true
	}

	for _, fromSig := range from.Signatures {
		to.Signatures = append(to.Signatures, NewSignature(fromSig))
	}
	for _, fromPkt := range from.Others {
		to.Unsupported = append(to.Unsupported, NewPacket(fromPkt))
	}

	return to
}

type PrimaryKey struct {
	*PublicKey

	MD5       string           `json:"md5"`
	SHA256    string           `json:"sha256,omitempty"`
	SubKeys   []*SubKey        `json:"subKeys,omitempty"`
	UserIDs   []*UserID        `json:"userIDs,omitempty"`
	UserAttrs []*UserAttribute `json:"userAttrs,omitempty"`
}

func NewPrimaryKeys(froms []*openpgp.PrimaryKey) []*PrimaryKey {
	var result []*PrimaryKey
	for _, from := range froms {
		result = append(result, NewPrimaryKey(from))
	}
	return result
}

func NewPrimaryKey(from *openpgp.PrimaryKey) *PrimaryKey {
	to := &PrimaryKey{
		PublicKey: newPublicKey(&from.PublicKey),
		MD5:       from.MD5,
		SHA256:    from.SHA256,
	}
	for _, fromSubKey := range from.SubKeys {
		to.SubKeys = append(to.SubKeys, NewSubKey(fromSubKey))
	}
	for _, fromUid := range from.UserIDs {
		to.UserIDs = append(to.UserIDs, NewUserID(fromUid))
	}
	for _, fromUat := range from.UserAttributes {
		to.UserAttrs = append(to.UserAttrs, NewUserAttribute(fromUat))
	}
	return to
}

func (pk *PrimaryKey) Serialize(w io.Writer) error {
	packets := pk.packets()
	for _, packet := range packets {
		_, err := w.Write(packet.Data)
		if err != nil {
			return errgo.Mask(err)
		}
	}
	return nil
}

type SubKey struct {
	*PublicKey
}

func NewSubKey(from *openpgp.SubKey) *SubKey {
	return &SubKey{
		newPublicKey(&from.PublicKey),
	}
}

type UserID struct {
	Keywords    string       `json:"keywords"`
	Packet      *Packet      `json:"packet,omitempty"`
	Signatures  []*Signature `json:"signatures,omitempty"`
	Unsupported []*Packet    `json:"unsupported,omitempty"`
}

func NewUserID(from *openpgp.UserID) *UserID {
	to := &UserID{
		Keywords: from.Keywords,
		Packet:   NewPacket(&from.Packet),
	}
	for _, fromSig := range from.Signatures {
		to.Signatures = append(to.Signatures, NewSignature(fromSig))
	}
	for _, fromPkt := range from.Others {
		to.Unsupported = append(to.Unsupported, NewPacket(fromPkt))
	}
	return to
}

type UserAttribute struct {
	Photos      []*Photo     `json:"photos,omitempty"`
	Packet      *Packet      `json:"packet,omitempty"`
	Signatures  []*Signature `json:"signatures,omitempty"`
	Unsupported []*Packet    `json:"unsupported,omitempty"`
}

func NewUserAttribute(from *openpgp.UserAttribute) *UserAttribute {
	to := &UserAttribute{
		Packet: NewPacket(&from.Packet),
	}
	for _, image := range from.Images {
		to.Photos = append(to.Photos, NewPhoto(image))
	}
	for _, fromSig := range from.Signatures {
		to.Signatures = append(to.Signatures, NewSignature(fromSig))
	}
	for _, fromPkt := range from.Others {
		to.Unsupported = append(to.Unsupported, NewPacket(fromPkt))
	}
	return to
}

type Photo struct {
	MIMEType string `json:"mimeType"`
	Contents []byte `json:"contents"`
}

func NewPhoto(image []byte) *Photo {
	return &Photo{
		MIMEType: "image/jpeg", // The only image format currently supported, AFAIK
		Contents: image,
	}
}

func (p *Photo) DataURI() (*url.URL, error) {
	return url.Parse(fmt.Sprintf(
		"data:%s;base64,%s", p.MIMEType, base64.StdEncoding.EncodeToString(p.Contents)))
}

type Signature struct {
	SigType      int     `json:"sigType"`
	Revocation   bool    `json:"revocation,omitempty"`
	Primary      bool    `json:"primary,omitempty"`
	IssuerKeyID  string  `json:"issuerKeyID,omitempty"`
	Creation     string  `json:"creation,omitempty"`
	Expiration   string  `json:"expiration,omitempty"`
	NeverExpires bool    `json:"neverExpires,omitempty"`
	Packet       *Packet `json:"packet,omitempty"`
}

func NewSignature(from *openpgp.Signature) *Signature {
	to := &Signature{
		Packet:      NewPacket(&from.Packet),
		SigType:     from.SigType,
		IssuerKeyID: from.IssuerKeyID(),
		Primary:     from.Primary,
	}

	switch to.SigType {
	case 0x20, 0x28, 0x30:
		to.Revocation = true
	}

	if !from.Creation.IsZero() {
		// can happen if openpgp.v1 isn't able to parse this type of signature
		to.Creation = from.Creation.UTC().Format(time.RFC3339)
	}

	if !from.Expiration.IsZero() {
		to.Expiration = from.Expiration.UTC().Format(time.RFC3339)
	} else {
		to.NeverExpires = true
	}

	return to
}

func (pk *PrimaryKey) Bytes() []byte {
	var buf []byte
	for _, pkt := range pk.packets() {
		buf = append(buf, pkt.Data...)
	}
	return buf
}

func (s *Signature) packets() []*Packet {
	packets := []*Packet{s.Packet}
	return packets
}

func (pk *PublicKey) packets() []*Packet {
	packets := []*Packet{pk.Packet}
	for _, s := range pk.Signatures {
		packets = append(packets, s.packets()...)
	}
	for _, un := range pk.Unsupported {
		packets = append(packets, un)
	}
	return packets
}

func (u *UserID) packets() []*Packet {
	packets := []*Packet{u.Packet}
	for _, s := range u.Signatures {
		packets = append(packets, s.packets()...)
	}
	for _, un := range u.Unsupported {
		packets = append(packets, un)
	}
	return packets
}

func (u *UserAttribute) packets() []*Packet {
	packets := []*Packet{u.Packet}
	for _, s := range u.Signatures {
		packets = append(packets, s.packets()...)
	}
	for _, un := range u.Unsupported {
		packets = append(packets, un)
	}
	return packets
}

func (pk *PrimaryKey) packets() []*Packet {
	packets := pk.PublicKey.packets()
	for _, u := range pk.UserIDs {
		packets = append(packets, u.packets()...)
	}
	for _, u := range pk.UserAttrs {
		packets = append(packets, u.packets()...)
	}
	for _, s := range pk.SubKeys {
		packets = append(packets, s.packets()...)
	}
	return packets
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"database/sql/driver"
	"errors"

	"github.com/lib/pq"
)

type ClassUnit struct {
	Id         sql.NullInt64
	Name       sql.NullString
	EnlistedOn pq.NullTime
	LeftOn     pq.NullTime
}

func (cu *ClassUnit) MarshalJSON() ([]byte, error) {
	return MarshalJSON(cu)
}

func (cu ClassUnit) Value() (driver.Value, error) {
	if cu.Id.Valid || cu.Name.Valid || cu.EnlistedOn.Valid || cu.LeftOn.Valid {
		return &cu, nil
	}

	return nil, nil
}
<commit_msg>[kami][ClassUnit] Remove unused dependency<commit_after>package main

import (
	"database/sql"
	"database/sql/driver"

	"github.com/lib/pq"
)

type ClassUnit struct {
	Id         sql.NullInt64
	Name       sql.NullString
	EnlistedOn pq.NullTime
	LeftOn     pq.NullTime
}

func (cu *ClassUnit) MarshalJSON() ([]byte, error) {
	return MarshalJSON(cu)
}

func (cu ClassUnit) Value() (driver.Value, error) {
	if cu.Id.Valid || cu.Name.Valid || cu.EnlistedOn.Valid || cu.LeftOn.Valid {
		return &cu, nil
	}

	return nil, nil
}
<|endoftext|>


<commit_before>package logging

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strings"

	"github.com/hashicorp/go-syslog"
	"github.com/hashicorp/logutils"
)

// Levels are the log levels we respond to=o.
var Levels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERR"}

// Config is the configuration for this log setup.
type Config struct {
	// Level is the log level to use.
	Level string `json:"level"`

	// Syslog and SyslogFacility are the syslog configuration options.
	Syslog         bool   `json:"syslog"`
	SyslogFacility string `json:"syslog_facility"`
	// SyslogName is the progname as it will appear in syslog output (if enabled).
	SyslogName     string `json:"name"`

	// Writer is the output where logs should go. If syslog is enabled, data will
	// be written to writer in addition to syslog.
	Writer io.Writer `json:"-"`
}

func Setup(config *Config) error {
	var logOutput io.Writer

	// Setup the default logging
	logFilter := NewLogFilter()
	logFilter.MinLevel = logutils.LogLevel(strings.ToUpper(config.Level))
	logFilter.Writer = config.Writer
	if !ValidateLevelFilter(logFilter.MinLevel, logFilter) {
		levels := make([]string, 0, len(logFilter.Levels))
		for _, level := range logFilter.Levels {
			levels = append(levels, string(level))
		}
		return fmt.Errorf("invalid log level %q, valid log levels are %s",
			config.Level, strings.Join(levels, ", "))
	}

	// Check if syslog is enabled
	if config.Syslog {
		log.Printf("[DEBUG] (logging) enabling syslog on %s", config.SyslogFacility)

		l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, config.SyslogName)
		if err != nil {
			return fmt.Errorf("error setting up syslog logger: %s", err)
		}
		syslog := &SyslogWrapper{l, logFilter}
		logOutput = io.MultiWriter(logFilter, syslog)
	} else {
		logOutput = io.MultiWriter(logFilter)
	}

	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.LUTC)
	log.SetOutput(logOutput)

	return nil
}

// NewLogFilter returns a LevelFilter that is configured with the log levels that
// we use.
func NewLogFilter() *logutils.LevelFilter {
	return &logutils.LevelFilter{
		Levels:   Levels,
		MinLevel: "WARN",
		Writer:   ioutil.Discard,
	}
}

// ValidateLevelFilter verifies that the log levels within the filter are valid.
func ValidateLevelFilter(min logutils.LogLevel, filter *logutils.LevelFilter) bool {
	for _, level := range filter.Levels {
		if level == min {
			return true
		}
	}
	return false
}
<commit_msg>Writer to log date / time in conistent format like in other places.<commit_after>package logging

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strings"
	"time"

	"github.com/hashicorp/go-syslog"
	"github.com/hashicorp/logutils"
)

// Levels are the log levels we respond to=o.
var Levels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERR"}

type logWriter struct {
}

// writer to output date / time in a standard format
func (writer logWriter) Write(bytes []byte) (int, error) {
	return fmt.Print(time.Now().Format("2006-01-02T15:04:05.000Z0700") + " " + string(bytes))
}

// Config is the configuration for this log setup.
type Config struct {
	// Level is the log level to use.
	Level string `json:"level"`

	// Syslog and SyslogFacility are the syslog configuration options.
	Syslog         bool   `json:"syslog"`
	SyslogFacility string `json:"syslog_facility"`
	// SyslogName is the progname as it will appear in syslog output (if enabled).
	SyslogName     string `json:"name"`

	// Writer is the output where logs should go. If syslog is enabled, data will
	// be written to writer in addition to syslog.
	Writer io.Writer `json:"-"`
}

func Setup(config *Config) error {
	var logOutput io.Writer

	log.SetFlags(0)
	log.SetOutput(new(logWriter))

	// Setup the default logging
	logFilter := NewLogFilter()
	logFilter.MinLevel = logutils.LogLevel(strings.ToUpper(config.Level))
	logFilter.Writer = config.Writer
	if !ValidateLevelFilter(logFilter.MinLevel, logFilter) {
		levels := make([]string, 0, len(logFilter.Levels))
		for _, level := range logFilter.Levels {
			levels = append(levels, string(level))
		}
		return fmt.Errorf("invalid log level %q, valid log levels are %s",
			config.Level, strings.Join(levels, ", "))
	}

	// Check if syslog is enabled
	if config.Syslog {
		log.Printf("[DEBUG] (logging) enabling syslog on %s", config.SyslogFacility)

		l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, config.SyslogName)
		if err != nil {
			return fmt.Errorf("error setting up syslog logger: %s", err)
		}
		syslog := &SyslogWrapper{l, logFilter}
		logOutput = io.MultiWriter(logFilter, syslog)
	} else {
		logOutput = io.MultiWriter(logFilter)
	}

	log.SetOutput(logOutput)
	log.SetOutput(new(logWriter))

	return nil
}

// NewLogFilter returns a LevelFilter that is configured with the log levels that
// we use.
func NewLogFilter() *logutils.LevelFilter {
	return &logutils.LevelFilter{
		Levels:   Levels,
		MinLevel: "WARN",
		Writer:   ioutil.Discard,
	}
}

// ValidateLevelFilter verifies that the log levels within the filter are valid.
func ValidateLevelFilter(min logutils.LogLevel, filter *logutils.LevelFilter) bool {
	for _, level := range filter.Levels {
		if level == min {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"os"
	"reflect"
	"sort"
	"testing"
	"time"
)

func FakeSocketWrite(f formatter, m *Metric) []string {
	var ret []string
	lc := make(chan *LabelSet)
	quit := make(chan bool)
	go m.EmitLabelSets(lc, quit)
	for {
		select {
		case l := <-lc:
			ret = append(ret, f(m, l))
		case <-quit:
			goto ret
		}
	}
ret:
	sort.Strings(ret)
	return ret
}

func TestMetricToCollectd(t *testing.T) {
	ts, terr := time.Parse("2006/01/02 15:04:05", "2012/07/24 10:14:00")
	if terr != nil {
		t.Errorf("time parse error: %s", terr)
	}
	hostname, herr := os.Hostname()
	if herr != nil {
		t.Errorf("hostname error: %s", herr)
	}

	scalar_metric := NewMetric("foo", "prog", Counter)
	d, _ := scalar_metric.GetDatum()
	d.Set(37, ts)
	r := FakeSocketWrite(MetricToCollectd, scalar_metric)
	expected := []string{"PUTVAL \"" + hostname + "/mtail-prog/counter-foo\" interval=60 1343124840:37\n"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}

	dimensioned_metric := NewMetric("bar", "prog", Gauge, "label")
	d, _ = dimensioned_metric.GetDatum("quux")
	d.Set(37, ts)
	d, _ = dimensioned_metric.GetDatum("snuh")
	d.Set(37, ts)
	r = FakeSocketWrite(MetricToCollectd, dimensioned_metric)
	expected = []string{
		"PUTVAL \"" + hostname + "/mtail-prog/gauge-bar-label-quux\" interval=60 1343124840:37\n",
		"PUTVAL \"" + hostname + "/mtail-prog/gauge-bar-label-snuh\" interval=60 1343124840:37\n"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}
}

func TestMetricToGraphite(t *testing.T) {
	ts, terr := time.Parse("2006/01/02 15:04:05", "2012/07/24 10:14:00")
	if terr != nil {
		t.Errorf("time parse error: %s", terr)
	}

	scalar_metric := NewMetric("foo", "prog", Counter)
	d, _ := scalar_metric.GetDatum()
	d.Set(37, ts)
	r := FakeSocketWrite(MetricToGraphite, scalar_metric)
	expected := []string{"prog.foo 37 1343124840\n"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}

	dimensioned_metric := NewMetric("bar", "prog", Gauge, "l")
	d, _ = dimensioned_metric.GetDatum("quux")
	d.Set(37, ts)
	d, _ = dimensioned_metric.GetDatum("snuh")
	d.Set(37, ts)
	r = FakeSocketWrite(MetricToGraphite, dimensioned_metric)
	expected = []string{
		"prog.bar.l.quux 37 1343124840\n",
		"prog.bar.l.snuh 37 1343124840\n"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}
}

func TestMetricToStatsd(t *testing.T) {
	ts, terr := time.Parse("2006/01/02 15:04:05", "2012/07/24 10:14:00")
	if terr != nil {
		t.Errorf("time parse error: %s", terr)
	}

	scalar_metric := NewMetric("foo", "prog", Counter)
	d, _ := scalar_metric.GetDatum()
	d.Set(37, ts)
	r := FakeSocketWrite(MetricToStatsd, scalar_metric)
	expected := []string{"prog.foo:37|c"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}

	dimensioned_metric := NewMetric("bar", "prog", Gauge, "l")
	d, _ = dimensioned_metric.GetDatum("quux")
	d.Set(37, ts)
	d, _ = dimensioned_metric.GetDatum("snuh")
	d.Set(42, ts)
	r = FakeSocketWrite(MetricToStatsd, dimensioned_metric)
	expected = []string{
		"prog.bar.l.quux:37|c",
		"prog.bar.l.snuh:42|c"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}
}

func TestCSVExport(t *testing.T) {
	ts := time.Now()
	m1 := NewMetric("foo", "prog", Counter)
	d, _ := m1.GetDatum()
	d.Set(37, ts)
	m2 := NewMetric("bar", "prog", Counter, "a", "b")
	d, _ = m2.GetDatum("1", "1")
	d.Set(37, ts)
	d, _ = m2.GetDatum("2", "2")
	d.Set(37, ts)
	b := bytes.NewBufferString("")
	c := csv.NewWriter(b)
	csvExporter(c, []*Metric{m1, m2})
	c.Flush()
	expected := fmt.Sprintf(
		"prog,foo,Counter,%s,37\n"+
			"prog,bar,Counter,a,1,b,1,%s,37\n"+
			"prog,bar,Counter,a,2,b,2,%s,37\n",
		ts, ts, ts)
	out := b.String()
	if !reflect.DeepEqual(expected, out) {
		t.Errorf("CSV doesn't match:\n\texpected\n%v\n\treceived %v", expected, out)
	}
}
<commit_msg>insert a newline in the correct location<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"os"
	"reflect"
	"sort"
	"testing"
	"time"
)

func FakeSocketWrite(f formatter, m *Metric) []string {
	var ret []string
	lc := make(chan *LabelSet)
	quit := make(chan bool)
	go m.EmitLabelSets(lc, quit)
	for {
		select {
		case l := <-lc:
			ret = append(ret, f(m, l))
		case <-quit:
			goto ret
		}
	}
ret:
	sort.Strings(ret)
	return ret
}

func TestMetricToCollectd(t *testing.T) {
	ts, terr := time.Parse("2006/01/02 15:04:05", "2012/07/24 10:14:00")
	if terr != nil {
		t.Errorf("time parse error: %s", terr)
	}
	hostname, herr := os.Hostname()
	if herr != nil {
		t.Errorf("hostname error: %s", herr)
	}

	scalar_metric := NewMetric("foo", "prog", Counter)
	d, _ := scalar_metric.GetDatum()
	d.Set(37, ts)
	r := FakeSocketWrite(MetricToCollectd, scalar_metric)
	expected := []string{"PUTVAL \"" + hostname + "/mtail-prog/counter-foo\" interval=60 1343124840:37\n"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}

	dimensioned_metric := NewMetric("bar", "prog", Gauge, "label")
	d, _ = dimensioned_metric.GetDatum("quux")
	d.Set(37, ts)
	d, _ = dimensioned_metric.GetDatum("snuh")
	d.Set(37, ts)
	r = FakeSocketWrite(MetricToCollectd, dimensioned_metric)
	expected = []string{
		"PUTVAL \"" + hostname + "/mtail-prog/gauge-bar-label-quux\" interval=60 1343124840:37\n",
		"PUTVAL \"" + hostname + "/mtail-prog/gauge-bar-label-snuh\" interval=60 1343124840:37\n"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}
}

func TestMetricToGraphite(t *testing.T) {
	ts, terr := time.Parse("2006/01/02 15:04:05", "2012/07/24 10:14:00")
	if terr != nil {
		t.Errorf("time parse error: %s", terr)
	}

	scalar_metric := NewMetric("foo", "prog", Counter)
	d, _ := scalar_metric.GetDatum()
	d.Set(37, ts)
	r := FakeSocketWrite(MetricToGraphite, scalar_metric)
	expected := []string{"prog.foo 37 1343124840\n"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}

	dimensioned_metric := NewMetric("bar", "prog", Gauge, "l")
	d, _ = dimensioned_metric.GetDatum("quux")
	d.Set(37, ts)
	d, _ = dimensioned_metric.GetDatum("snuh")
	d.Set(37, ts)
	r = FakeSocketWrite(MetricToGraphite, dimensioned_metric)
	expected = []string{
		"prog.bar.l.quux 37 1343124840\n",
		"prog.bar.l.snuh 37 1343124840\n"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}
}

func TestMetricToStatsd(t *testing.T) {
	ts, terr := time.Parse("2006/01/02 15:04:05", "2012/07/24 10:14:00")
	if terr != nil {
		t.Errorf("time parse error: %s", terr)
	}

	scalar_metric := NewMetric("foo", "prog", Counter)
	d, _ := scalar_metric.GetDatum()
	d.Set(37, ts)
	r := FakeSocketWrite(MetricToStatsd, scalar_metric)
	expected := []string{"prog.foo:37|c"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}

	dimensioned_metric := NewMetric("bar", "prog", Gauge, "l")
	d, _ = dimensioned_metric.GetDatum("quux")
	d.Set(37, ts)
	d, _ = dimensioned_metric.GetDatum("snuh")
	d.Set(42, ts)
	r = FakeSocketWrite(MetricToStatsd, dimensioned_metric)
	expected = []string{
		"prog.bar.l.quux:37|c",
		"prog.bar.l.snuh:42|c"}
	if !reflect.DeepEqual(expected, r) {
		t.Errorf("String didn't match:\n\texpected: %v\n\treceived: %v", expected, r)
	}
}

func TestCSVExport(t *testing.T) {
	ts := time.Now()
	m1 := NewMetric("foo", "prog", Counter)
	d, _ := m1.GetDatum()
	d.Set(37, ts)
	m2 := NewMetric("bar", "prog", Counter, "a", "b")
	d, _ = m2.GetDatum("1", "1")
	d.Set(37, ts)
	d, _ = m2.GetDatum("2", "2")
	d.Set(37, ts)
	b := bytes.NewBufferString("")
	c := csv.NewWriter(b)
	csvExporter(c, []*Metric{m1, m2})
	c.Flush()
	expected := fmt.Sprintf(
		"prog,foo,Counter,%s,37\n"+
			"prog,bar,Counter,a,1,b,1,%s,37\n"+
			"prog,bar,Counter,a,2,b,2,%s,37\n",
		ts, ts, ts)
	out := b.String()
	if !reflect.DeepEqual(expected, out) {
		t.Errorf("CSV doesn't match:\n\texpected\n%v\n\treceived\n%v", expected, out)
	}
}
<|endoftext|>