<commit_before>// Copyright 2015-present Oursky Ltd.
//
// 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 skyconv

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"time"

	"github.com/skygeario/skygear-server/pkg/server/skydb"
)

// MapFrom tries to map a map to a FromMapper
func MapFrom(i interface{}, fromMapper FromMapper) error {
	if m, ok := i.(map[string]interface{}); ok {
		return fromMapper.FromMap(m)
	}

	return fmt.Errorf("want map, got type = %T", i)
}

// FromMapper defines whether a type can be converted from a map
type FromMapper interface {
	FromMap(m map[string]interface{}) error
}

// ToMapper defines whether a type can be converted to a map
type ToMapper interface {
	ToMap(m map[string]interface{})
}

// ToMap converts a ToMapper to map and returns it
func ToMap(mapper ToMapper) map[string]interface{} {
	mm := map[string]interface{}{}
	mapper.ToMap(mm)
	return mm
}

// MapData is record data that can be converted from a map
type MapData map[string]interface{}

// FromMap implements FromMapper
func (data *MapData) FromMap(m map[string]interface{}) (err error) {
	var walkedData map[string]interface{}
	walkedData, err = walkData(m)
	if err != nil {
		return
	}

	*data = walkedData
	return nil
}

// ToMap implements ToMapper
func (data MapData) ToMap(m map[string]interface{}) {
	for key, value := range data {
		if mapper, ok := value.(ToMapper); ok {
			mm := map[string]interface{}{}
			mapper.ToMap(mm)
			m[key] = mm
		} else {
			m[key] = value
		}
	}
}

// MapTime is time.Time that can be converted from and to a map.
type MapTime time.Time

// FromMap implements FromMapper
func (t *MapTime) FromMap(m map[string]interface{}) error {
	datei, ok := m["$date"]
	if !ok {
		return errors.New("missing compulsory field $date")
	}
	dateStr, ok := datei.(string)
	if !ok {
		return fmt.Errorf("got type($date) = %T, want string", datei)
	}
	dt, err := time.Parse(time.RFC3339Nano, dateStr)
	if err != nil {
		return fmt.Errorf("failed to parse $date = %#v", dateStr)
	}

	*(*time.Time)(t) = dt.In(time.UTC)
	return nil
}

// ToMap implements ToMapper
func (t MapTime) ToMap(m map[string]interface{}) {
	m["$type"] = "date"
	m["$date"] = time.Time(t)
}

// MapAsset is skydb.Asset that can be converted from and to a map.
type MapAsset skydb.Asset

// FromMap implements FromMapper
func (asset *MapAsset) FromMap(m map[string]interface{}) error {
	namei, ok := m["$name"]
	if !ok {
		return errors.New("missing compulsory field $name")
	}
	name, ok := namei.(string)
	if !ok {
		return fmt.Errorf("got type($name) = %T, want string", namei)
	}
	if name == "" {
		return errors.New("asset's $name should not be empty")
	}

	asset.Name = name
	return nil
}

// ToMap implements ToMapper
func (asset *MapAsset) ToMap(m map[string]interface{}) {
	m["$type"] = "asset"
	m["$name"] = asset.Name
	m["$content_type"] = asset.ContentType
	url := (*skydb.Asset)(asset).SignedURL()
	if url != "" {
		m["$url"] = url
	}
}

// MapReference is skydb.Reference that can be converted from and to a map.
type MapReference skydb.Reference

// FromMap implements FromMapper
func (ref *MapReference) FromMap(m map[string]interface{}) error {
	idi, ok := m["$id"]
	if !ok {
		return errors.New("referencing without $id")
	}
	id, ok := idi.(string)
	if !ok {
		return fmt.Errorf("got reference type($id) = %T, want string", idi)
	}
	ss := strings.SplitN(id, "/", 2)
	if len(ss) == 1 {
		return fmt.Errorf(`ref: "_id" should be of format '{type}/{id}', got %#v`, id)
	}

	ref.ID.Type = ss[0]
	ref.ID.Key = ss[1]
	return nil
}

// ToMap implements ToMapper
func (ref MapReference) ToMap(m map[string]interface{}) {
	m["$type"] = "ref"
	m["$id"] = ref.ID
}

// MapLocation is skydb.Location that can be converted from and to a map.
type MapLocation skydb.Location

// FromMap implements FromMapper
func (loc *MapLocation) FromMap(m map[string]interface{}) error {
	getFloat := func(m map[string]interface{}, key string) (float64, error) {
		i, ok := m[key]
		if !ok {
			return 0, fmt.Errorf("missing compulsory field %s", key)
		}

		f, ok := i.(float64)
		if !ok {
			return 0, fmt.Errorf("got type(%s) = %T, want number", key, i)
		}

		return f, nil
	}

	lng, err := getFloat(m, "$lng")
	if err != nil {
		return err
	}

	lat, err := getFloat(m, "$lat")
	if err != nil {
		return err
	}

	*loc = MapLocation{lng, lat}
	return nil
}

// ToMap implements ToMapper
func (loc MapLocation) ToMap(m map[string]interface{}) {
	m["$type"] = "geo"
	m["$lng"] = loc[0]
	m["$lat"] = loc[1]
}

func walkData(m map[string]interface{}) (mapReturned map[string]interface{}, err error) {
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(runtime.Error); ok {
				panic(r)
			}
			err = r.(error)
		}
	}()

	return walkMap(m), err
}

// MapKeyPath is string keypath that can be converted from a map
type MapKeyPath string

// FromMap implements FromMapper
func (p *MapKeyPath) FromMap(m map[string]interface{}) error {
	keyPath, _ := m["$val"].(string)
	if keyPath == "" {
		return errors.New("empty key path")
	}

	*p = MapKeyPath(keyPath)
	return nil
}

// ToMap implements ToMapper
func (p MapKeyPath) ToMap(m map[string]interface{}) {
	m["$type"] = "keypath"
	m["$val"] = string(p)
}

// MapRelation is a type specifying a relation between two users, but do not conform to any actual struct in skydb.
type MapRelation struct {
	Name      string
	Direction string
}

// FromMap implements FromMapper
func (rel *MapRelation) FromMap(m map[string]interface{}) error {
	name, _ := m["$name"].(string)
	if name == "" {
		return errors.New("empty relation name")
	}

	direction, _ := m["$direction"].(string)
	if direction == "" {
		return errors.New("empty direction")
	}

	*rel = MapRelation{name, direction}
	return nil
}

// ToMap implements ToMapper
func (rel *MapRelation) ToMap(m map[string]interface{}) {
	m["$type"] = "relation"
	m["$name"] = rel.Name
	m["$direction"] = rel.Direction
}

// MapSequence is skydb.Sequence that can convert to map
type MapSequence struct{}

// ToMap implements ToMapper
func (seq MapSequence) ToMap(m map[string]interface{}) {
	m["$type"] = "seq"
}

// MapUnknown is skydb.Unknown that can convert to map
type MapUnknown skydb.Unknown

// FromMap implements FromMapper
func (val *MapUnknown) FromMap(m map[string]interface{}) error {
	underlyingType, _ := m["$underlying_type"].(string)
	*val = MapUnknown{underlyingType}
	return nil
}

// ToMap implements ToMapper
func (val MapUnknown) ToMap(m map[string]interface{}) {
	m["$type"] = "unknown"
	m["$underlying_type"] = val.UnderlyingType
}

type MapACLEntry skydb.RecordACLEntry

// FromMap initializes a RecordACLEntry from a unmarshalled JSON of
// access control definition
func (ace *MapACLEntry) FromMap(m map[string]interface{}) error {
	level, _ := m["level"].(string)
	var entryLevel skydb.ACLLevel
	switch level {
	case "read":
		entryLevel = skydb.ReadLevel
	case "write":
		entryLevel = skydb.WriteLevel
	case "":
		return errors.New("empty level")
	default:
		return fmt.Errorf("unknown level = %s", level)
	}

	relation, hasRelation := m["relation"].(string)
	userID, hasUserID := m["user_id"].(string)
	role, hasRole := m["role"].(string)
	public, hasPublic := m["public"].(bool)
	if !hasRelation && !hasUserID && !hasRole && !hasPublic {
		return errors.New("ACLEntry must have relation, user_id, role or public")
	}

	ace.Level = entryLevel
	if hasRelation {
		ace.Relation = relation
	}
	if hasRole {
		ace.Role = role
	}
	if hasUserID {
		ace.UserID = userID
	}
	if hasPublic {
		ace.Public = public
	}
	return nil
}

func walkMap(m map[string]interface{}) map[string]interface{} {
	for key, value := range m {
		m[key] = ParseLiteral(value)
	}

	return m
}

func walkSlice(items []interface{}) []interface{} {
	for i, item := range items {
		items[i] = ParseLiteral(item)
	}

	return items
}

// ParseLiteral deduces whether i is a skydb data value and returns a
// parsed value.
func ParseLiteral(i interface{}) interface{} {
	switch value := i.(type) {
	default:
		// considered a bug if this line is reached
		panic(fmt.Errorf("unsupported value = %T", value))
	case nil, bool, float64, string:
		// the set of value that json unmarshaller returns
		// http://golang.org/pkg/encoding/json/#Unmarshal
		return value
	case map[string]interface{}:
		kindi, typed := value["$type"]
		if !typed {
			// regular dictionary, go deeper
			return walkMap(value)
		}

		kind, ok := kindi.(string)
		if !ok {
			panic(fmt.Errorf(`got "$type"'s type = %T, want string`, kindi))
		}

		switch kind {
		case "keypath":
			var keyPath string
			mapFromOrPanic((*MapKeyPath)(&keyPath), value)
			return keyPath
		case "blob":
			panic(fmt.Errorf("unimplemented $type = %s", kind))
		case "asset":
			var asset skydb.Asset
			mapFromOrPanic((*MapAsset)(&asset), value)
			return &asset
		case "ref":
			var ref skydb.Reference
			mapFromOrPanic((*MapReference)(&ref), value)
			return ref
		case "date":
			var t time.Time
			mapFromOrPanic((*MapTime)(&t), value)
			return t
		case "geo":
			var loc skydb.Location
			mapFromOrPanic((*MapLocation)(&loc), value)
			return loc
		case "seq":
			return skydb.Sequence{}
		case "unknown":
			var val skydb.Unknown
			mapFromOrPanic((*MapUnknown)(&val), value)
			return val
		case "relation":
			var rel MapRelation
			mapFromOrPanic((*MapRelation)(&rel), value)
			return &rel
		default:
			panic(fmt.Errorf("unknown $type = %s", kind))
		}
	case []interface{}:
		return walkSlice(value)
	}
}

func mapFromOrPanic(fromMapper FromMapper, m map[string]interface{}) {
	if err := fromMapper.FromMap(m); err != nil {
		panic(err)
	}
}
<commit_msg>Read content type from JSON map to model<commit_after>// Copyright 2015-present Oursky Ltd.
//
// 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 skyconv

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
	"time"

	"github.com/skygeario/skygear-server/pkg/server/skydb"
)

// MapFrom tries to map a map to a FromMapper
func MapFrom(i interface{}, fromMapper FromMapper) error {
	if m, ok := i.(map[string]interface{}); ok {
		return fromMapper.FromMap(m)
	}

	return fmt.Errorf("want map, got type = %T", i)
}

// FromMapper defines whether a type can be converted from a map
type FromMapper interface {
	FromMap(m map[string]interface{}) error
}

// ToMapper defines whether a type can be converted to a map
type ToMapper interface {
	ToMap(m map[string]interface{})
}

// ToMap converts a ToMapper to map and returns it
func ToMap(mapper ToMapper) map[string]interface{} {
	mm := map[string]interface{}{}
	mapper.ToMap(mm)
	return mm
}

// MapData is record data that can be converted from a map
type MapData map[string]interface{}

// FromMap implements FromMapper
func (data *MapData) FromMap(m map[string]interface{}) (err error) {
	var walkedData map[string]interface{}
	walkedData, err = walkData(m)
	if err != nil {
		return
	}

	*data = walkedData
	return nil
}

// ToMap implements ToMapper
func (data MapData) ToMap(m map[string]interface{}) {
	for key, value := range data {
		if mapper, ok := value.(ToMapper); ok {
			mm := map[string]interface{}{}
			mapper.ToMap(mm)
			m[key] = mm
		} else {
			m[key] = value
		}
	}
}

// MapTime is time.Time that can be converted from and to a map.
type MapTime time.Time

// FromMap implements FromMapper
func (t *MapTime) FromMap(m map[string]interface{}) error {
	datei, ok := m["$date"]
	if !ok {
		return errors.New("missing compulsory field $date")
	}
	dateStr, ok := datei.(string)
	if !ok {
		return fmt.Errorf("got type($date) = %T, want string", datei)
	}
	dt, err := time.Parse(time.RFC3339Nano, dateStr)
	if err != nil {
		return fmt.Errorf("failed to parse $date = %#v", dateStr)
	}

	*(*time.Time)(t) = dt.In(time.UTC)
	return nil
}

// ToMap implements ToMapper
func (t MapTime) ToMap(m map[string]interface{}) {
	m["$type"] = "date"
	m["$date"] = time.Time(t)
}

// MapAsset is skydb.Asset that can be converted from and to a map.
type MapAsset skydb.Asset

// FromMap implements FromMapper
func (asset *MapAsset) FromMap(m map[string]interface{}) error {
	namei, ok := m["$name"]
	if !ok {
		return errors.New("missing compulsory field $name")
	}
	name, ok := namei.(string)
	if !ok {
		return fmt.Errorf("got type($name) = %T, want string", namei)
	}
	if name == "" {
		return errors.New("asset's $name should not be empty")
	}

	asset.Name = name

	contentTypei, ok := m["$content_type"]
	if !ok {
		return errors.New("missing compulsory field $content_type")
	}
	contentType, ok := contentTypei.(string)
	if !ok {
		return fmt.Errorf("got type($contentType) = %T, want string", contentTypei)
	}
	if contentType == "" {
		return errors.New("asset's $contentType should not be empty")
	}

	asset.ContentType = contentType
	return nil
}

// ToMap implements ToMapper
func (asset *MapAsset) ToMap(m map[string]interface{}) {
	m["$type"] = "asset"
	m["$name"] = asset.Name
	m["$content_type"] = asset.ContentType
	url := (*skydb.Asset)(asset).SignedURL()
	if url != "" {
		m["$url"] = url
	}
}

// MapReference is skydb.Reference that can be converted from and to a map.
type MapReference skydb.Reference

// FromMap implements FromMapper
func (ref *MapReference) FromMap(m map[string]interface{}) error {
	idi, ok := m["$id"]
	if !ok {
		return errors.New("referencing without $id")
	}
	id, ok := idi.(string)
	if !ok {
		return fmt.Errorf("got reference type($id) = %T, want string", idi)
	}
	ss := strings.SplitN(id, "/", 2)
	if len(ss) == 1 {
		return fmt.Errorf(`ref: "_id" should be of format '{type}/{id}', got %#v`, id)
	}

	ref.ID.Type = ss[0]
	ref.ID.Key = ss[1]
	return nil
}

// ToMap implements ToMapper
func (ref MapReference) ToMap(m map[string]interface{}) {
	m["$type"] = "ref"
	m["$id"] = ref.ID
}

// MapLocation is skydb.Location that can be converted from and to a map.
type MapLocation skydb.Location

// FromMap implements FromMapper
func (loc *MapLocation) FromMap(m map[string]interface{}) error {
	getFloat := func(m map[string]interface{}, key string) (float64, error) {
		i, ok := m[key]
		if !ok {
			return 0, fmt.Errorf("missing compulsory field %s", key)
		}

		f, ok := i.(float64)
		if !ok {
			return 0, fmt.Errorf("got type(%s) = %T, want number", key, i)
		}

		return f, nil
	}

	lng, err := getFloat(m, "$lng")
	if err != nil {
		return err
	}

	lat, err := getFloat(m, "$lat")
	if err != nil {
		return err
	}

	*loc = MapLocation{lng, lat}
	return nil
}

// ToMap implements ToMapper
func (loc MapLocation) ToMap(m map[string]interface{}) {
	m["$type"] = "geo"
	m["$lng"] = loc[0]
	m["$lat"] = loc[1]
}

func walkData(m map[string]interface{}) (mapReturned map[string]interface{}, err error) {
	defer func() {
		if r := recover(); r != nil {
			if _, ok := r.(runtime.Error); ok {
				panic(r)
			}
			err = r.(error)
		}
	}()

	return walkMap(m), err
}

// MapKeyPath is string keypath that can be converted from a map
type MapKeyPath string

// FromMap implements FromMapper
func (p *MapKeyPath) FromMap(m map[string]interface{}) error {
	keyPath, _ := m["$val"].(string)
	if keyPath == "" {
		return errors.New("empty key path")
	}

	*p = MapKeyPath(keyPath)
	return nil
}

// ToMap implements ToMapper
func (p MapKeyPath) ToMap(m map[string]interface{}) {
	m["$type"] = "keypath"
	m["$val"] = string(p)
}

// MapRelation is a type specifying a relation between two users, but do not conform to any actual struct in skydb.
type MapRelation struct {
	Name      string
	Direction string
}

// FromMap implements FromMapper
func (rel *MapRelation) FromMap(m map[string]interface{}) error {
	name, _ := m["$name"].(string)
	if name == "" {
		return errors.New("empty relation name")
	}

	direction, _ := m["$direction"].(string)
	if direction == "" {
		return errors.New("empty direction")
	}

	*rel = MapRelation{name, direction}
	return nil
}

// ToMap implements ToMapper
func (rel *MapRelation) ToMap(m map[string]interface{}) {
	m["$type"] = "relation"
	m["$name"] = rel.Name
	m["$direction"] = rel.Direction
}

// MapSequence is skydb.Sequence that can convert to map
type MapSequence struct{}

// ToMap implements ToMapper
func (seq MapSequence) ToMap(m map[string]interface{}) {
	m["$type"] = "seq"
}

// MapUnknown is skydb.Unknown that can convert to map
type MapUnknown skydb.Unknown

// FromMap implements FromMapper
func (val *MapUnknown) FromMap(m map[string]interface{}) error {
	underlyingType, _ := m["$underlying_type"].(string)
	*val = MapUnknown{underlyingType}
	return nil
}

// ToMap implements ToMapper
func (val MapUnknown) ToMap(m map[string]interface{}) {
	m["$type"] = "unknown"
	m["$underlying_type"] = val.UnderlyingType
}

type MapACLEntry skydb.RecordACLEntry

// FromMap initializes a RecordACLEntry from a unmarshalled JSON of
// access control definition
func (ace *MapACLEntry) FromMap(m map[string]interface{}) error {
	level, _ := m["level"].(string)
	var entryLevel skydb.ACLLevel
	switch level {
	case "read":
		entryLevel = skydb.ReadLevel
	case "write":
		entryLevel = skydb.WriteLevel
	case "":
		return errors.New("empty level")
	default:
		return fmt.Errorf("unknown level = %s", level)
	}

	relation, hasRelation := m["relation"].(string)
	userID, hasUserID := m["user_id"].(string)
	role, hasRole := m["role"].(string)
	public, hasPublic := m["public"].(bool)
	if !hasRelation && !hasUserID && !hasRole && !hasPublic {
		return errors.New("ACLEntry must have relation, user_id, role or public")
	}

	ace.Level = entryLevel
	if hasRelation {
		ace.Relation = relation
	}
	if hasRole {
		ace.Role = role
	}
	if hasUserID {
		ace.UserID = userID
	}
	if hasPublic {
		ace.Public = public
	}
	return nil
}

func walkMap(m map[string]interface{}) map[string]interface{} {
	for key, value := range m {
		m[key] = ParseLiteral(value)
	}

	return m
}

func walkSlice(items []interface{}) []interface{} {
	for i, item := range items {
		items[i] = ParseLiteral(item)
	}

	return items
}

// ParseLiteral deduces whether i is a skydb data value and returns a
// parsed value.
func ParseLiteral(i interface{}) interface{} {
	switch value := i.(type) {
	default:
		// considered a bug if this line is reached
		panic(fmt.Errorf("unsupported value = %T", value))
	case nil, bool, float64, string:
		// the set of value that json unmarshaller returns
		// http://golang.org/pkg/encoding/json/#Unmarshal
		return value
	case map[string]interface{}:
		kindi, typed := value["$type"]
		if !typed {
			// regular dictionary, go deeper
			return walkMap(value)
		}

		kind, ok := kindi.(string)
		if !ok {
			panic(fmt.Errorf(`got "$type"'s type = %T, want string`, kindi))
		}

		switch kind {
		case "keypath":
			var keyPath string
			mapFromOrPanic((*MapKeyPath)(&keyPath), value)
			return keyPath
		case "blob":
			panic(fmt.Errorf("unimplemented $type = %s", kind))
		case "asset":
			var asset skydb.Asset
			mapFromOrPanic((*MapAsset)(&asset), value)
			return &asset
		case "ref":
			var ref skydb.Reference
			mapFromOrPanic((*MapReference)(&ref), value)
			return ref
		case "date":
			var t time.Time
			mapFromOrPanic((*MapTime)(&t), value)
			return t
		case "geo":
			var loc skydb.Location
			mapFromOrPanic((*MapLocation)(&loc), value)
			return loc
		case "seq":
			return skydb.Sequence{}
		case "unknown":
			var val skydb.Unknown
			mapFromOrPanic((*MapUnknown)(&val), value)
			return val
		case "relation":
			var rel MapRelation
			mapFromOrPanic((*MapRelation)(&rel), value)
			return &rel
		default:
			panic(fmt.Errorf("unknown $type = %s", kind))
		}
	case []interface{}:
		return walkSlice(value)
	}
}

func mapFromOrPanic(fromMapper FromMapper, m map[string]interface{}) {
	if err := fromMapper.FromMap(m); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package alerting

import (
	"sync"
	"time"

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

type RuleReader interface {
	Fetch() []*Rule
}

type DefaultRuleReader struct {
	sync.RWMutex
	//serverID       string
	serverPosition int
	clusterSize    int
	log            log.Logger
}

func NewRuleReader() *DefaultRuleReader {
	ruleReader := &DefaultRuleReader{
		log: log.New("alerting.ruleReader"),
	}

	go ruleReader.initReader()
	return ruleReader
}

func (arr *DefaultRuleReader) initReader() {
	heartbeat := time.NewTicker(time.Second * 10)

	for {
		select {
		case <-heartbeat.C:
			arr.heartbeat()
		}
	}
}

func (arr *DefaultRuleReader) Fetch() []*Rule {
	cmd := &m.GetAllAlertsQuery{}

	if err := bus.Dispatch(cmd); err != nil {
		arr.log.Error("Could not load alerts", "error", err)
		return []*Rule{}
	}

	res := make([]*Rule, 0)
	for _, ruleDef := range cmd.Result {
		if model, err := NewRuleFromDBAlert(ruleDef); err != nil {
			arr.log.Error("Could not build alert model for rule", "ruleId", ruleDef.Id, "error", err)
		} else {
			res = append(res, model)
		}
	}

	metrics.M_Alerting_Active_Alerts.Set(float64(len(res)))
	return res
}

func (arr *DefaultRuleReader) heartbeat() {
	arr.clusterSize = 1
	arr.serverPosition = 1
}
<commit_msg>pkg/services/alerting/reader.go: Fix should use for range instead of for { select {} }.<commit_after>package alerting

import (
	"sync"
	"time"

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

type RuleReader interface {
	Fetch() []*Rule
}

type DefaultRuleReader struct {
	sync.RWMutex
	//serverID       string
	serverPosition int
	clusterSize    int
	log            log.Logger
}

func NewRuleReader() *DefaultRuleReader {
	ruleReader := &DefaultRuleReader{
		log: log.New("alerting.ruleReader"),
	}

	go ruleReader.initReader()
	return ruleReader
}

func (arr *DefaultRuleReader) initReader() {
	heartbeat := time.NewTicker(time.Second * 10)

	for range heartbeat.C {
		arr.heartbeat()
	}
}

func (arr *DefaultRuleReader) Fetch() []*Rule {
	cmd := &m.GetAllAlertsQuery{}

	if err := bus.Dispatch(cmd); err != nil {
		arr.log.Error("Could not load alerts", "error", err)
		return []*Rule{}
	}

	res := make([]*Rule, 0)
	for _, ruleDef := range cmd.Result {
		if model, err := NewRuleFromDBAlert(ruleDef); err != nil {
			arr.log.Error("Could not build alert model for rule", "ruleId", ruleDef.Id, "error", err)
		} else {
			res = append(res, model)
		}
	}

	metrics.M_Alerting_Active_Alerts.Set(float64(len(res)))
	return res
}

func (arr *DefaultRuleReader) heartbeat() {
	arr.clusterSize = 1
	arr.serverPosition = 1
}
<|endoftext|>


<commit_before>package config

import (
	"testing"

	"github.com/minio-io/minio/pkg/utils/crypto/keys"
	. "gopkg.in/check.v1"
)

type MySuite struct{}

var _ = Suite(&MySuite{})

func Test(t *testing.T) { TestingT(t) }

func (s *MySuite) TestConfig(c *C) {
	conf := Config{}
	conf.SetupConfig()

	accesskey, _ := keys.GetRandomAlphaNumeric(keys.MINIO_ACCESS_ID)
	secretkey, _ := keys.GetRandomBase64(keys.MINIO_SECRET_ID)

	user := User{
		Name:      "gnubot",
		AccessKey: string(accesskey),
		SecretKey: string(secretkey),
	}

	conf.AddUser(user)
	err := conf.WriteConfig()
	c.Assert(err, IsNil)

	err = conf.ReadConfig()
	c.Assert(err, IsNil)

	accesskey, _ = keys.GetRandomAlphaNumeric(keys.MINIO_ACCESS_ID)
	secretkey, _ = keys.GetRandomBase64(keys.MINIO_SECRET_ID)
	user = User{
		Name:      "minio",
		AccessKey: string(accesskey),
		SecretKey: string(secretkey),
	}
	conf.AddUser(user)
	err = conf.WriteConfig()
	c.Assert(err, IsNil)
}
<commit_msg>Make sure config testing is done in tempdir<commit_after>package config

import (
	"os"
	"path"
	"sync"
	"testing"

	"github.com/minio-io/minio/pkg/utils/crypto/keys"
	"github.com/minio-io/minio/pkg/utils/helpers"
	. "gopkg.in/check.v1"
)

type MySuite struct{}

var _ = Suite(&MySuite{})

func Test(t *testing.T) { TestingT(t) }

func (s *MySuite) TestConfig(c *C) {
	conf := Config{}
	conf.configPath, _ = helpers.MakeTempTestDir()
	defer os.RemoveAll(conf.configPath)
	conf.configFile = path.Join(conf.configPath, "config.json")
	if _, err := os.Stat(conf.configFile); os.IsNotExist(err) {
		_, err = os.Create(conf.configFile)
		if err != nil {
			c.Fatal(err)
		}
	}
	conf.configLock = new(sync.RWMutex)

	accesskey, _ := keys.GetRandomAlphaNumeric(keys.MINIO_ACCESS_ID)
	secretkey, _ := keys.GetRandomBase64(keys.MINIO_SECRET_ID)

	user := User{
		Name:      "gnubot",
		AccessKey: string(accesskey),
		SecretKey: string(secretkey),
	}

	conf.AddUser(user)
	err := conf.WriteConfig()
	c.Assert(err, IsNil)

	err = conf.ReadConfig()
	c.Assert(err, IsNil)

	accesskey, _ = keys.GetRandomAlphaNumeric(keys.MINIO_ACCESS_ID)
	secretkey, _ = keys.GetRandomBase64(keys.MINIO_SECRET_ID)
	user = User{
		Name:      "minio",
		AccessKey: string(accesskey),
		SecretKey: string(secretkey),
	}
	conf.AddUser(user)
	err = conf.WriteConfig()
	c.Assert(err, IsNil)
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package adapter

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"time"

	containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
	"cloud.google.com/go/storage"
	"github.com/pkg/errors"
	"golang.org/x/net/html"
	"google.golang.org/api/iterator"
	grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
)

func uploadFile(directory, filename, bucket string) error {
	const timeout = 60
	ctx := context.Background()
	client, err := storage.NewClient(ctx)
	if err != nil {
		return errors.Errorf("storage.NewClient: %v", err)
	}
	defer client.Close()

	// Open local file
	f, err := os.Open(directory + filename)
	if err != nil {
		return errors.Errorf("os.Open: %v", err)
	}
	defer f.Close()

	ctx, cancel := context.WithTimeout(ctx, time.Second*timeout)
	defer cancel()

	// Upload the object with storage.Writer
	wc := client.Bucket(bucket).Object(filename).NewWriter(ctx)
	if _, err = io.Copy(wc, f); err != nil {
		return errors.Errorf("io.Copy: %v", err)
	}
	if err := wc.Close(); err != nil {
		return errors.Errorf("Writer.Close: %v", err)
	}
	return nil
}

// GetAllVulnerabilities gets all of the vulnerability occurrences associated
// with images in a specific project using the Container Analysis Service.
func GetAllVulnerabilities(
	projectID string,
) ([]*grafeaspb.Occurrence, error) {
	ctx := context.Background()
	client, err := containeranalysis.NewClient(ctx)
	if err != nil {
		return nil, errors.Errorf("NewClient: %v", err)
	}
	defer client.Close()

	req := &grafeaspb.ListOccurrencesRequest{
		Parent: fmt.Sprintf("projects/%s", projectID),
		Filter: fmt.Sprintf("kind = %q", "VULNERABILITY"),
	}

	var occurrenceList []*grafeaspb.Occurrence
	it := client.GetGrafeasClient().ListOccurrences(ctx, req)
	for {
		occ, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, errors.Errorf("occurrence iteration error: %v", err)
		}
		occurrenceList = append(occurrenceList, occ)
	}
	return occurrenceList, err
}

func parseImageResourceURL(resourceURL string) (registryImageName, digest string) {
	FQIN := path.Base(resourceURL)
	splitFQIN := strings.Split(FQIN, "@")
	registryImageName, digest = splitFQIN[0], splitFQIN[1]

	return registryImageName, digest
}

func parseVulnName(noteName string) string {
	return path.Base(noteName)
}

// GenerateVulnerabilityBreakdown parses the a slice of vulnerability
// occurrences into a breakdown that only contains the necessary information
// for each vulnerability.
func GenerateVulnerabilityBreakdown(
	productionVulnerabilities []*grafeaspb.Occurrence,
) map[string]ImageVulnBreakdown {
	vulnBreakdowns := make(map[string]ImageVulnBreakdown)

	for _, occ := range productionVulnerabilities {
		// resourceURI is a url pointing to a specific image
		// in the form gcr.io/project/foo@sha256:111
		if _, found := vulnBreakdowns[occ.ResourceUri]; !found {
			imageName, imageDigest := parseImageResourceURL(occ.ResourceUri)
			vulnBreakdowns[occ.ResourceUri] = ImageVulnBreakdown{
				occ.ResourceUri,
				imageName,
				imageDigest,
				0,
				[]string{},
				[]string{},
			}
		}

		imageVulnBreakdown := vulnBreakdowns[occ.ResourceUri]
		imageVulnBreakdown.NumVulnerabilities++

		vulnName := parseVulnName(occ.NoteName)
		vuln := occ.GetVulnerability()
		if vuln.GetSeverity() == grafeaspb.Severity_CRITICAL {
			imageVulnBreakdown.CriticalVulnerabilities = append(
				imageVulnBreakdown.CriticalVulnerabilities,
				vulnName,
			)
		}
		if vuln.GetFixAvailable() {
			imageVulnBreakdown.FixableVulnerabilities = append(
				imageVulnBreakdown.FixableVulnerabilities,
				vulnName,
			)
		}
		vulnBreakdowns[occ.ResourceUri] = imageVulnBreakdown
	}

	return vulnBreakdowns
}

// UpdateVulnerabilityDashboard updates the vulnerability dashboard by uploading
// the lastest versions of all the vulnerability dashboard's files.
func UpdateVulnerabilityDashboard(
	dashboardPath string,
	vulnProject string,
	dashboardBucket string,
) error {
	htmlReader, openErr := os.Open(dashboardPath + "dashboard.html")
	if openErr != nil {
		return errors.Wrap(openErr, "opening dashboard file")
	}

	_, err := html.Parse(htmlReader)
	if err != nil {
		return errors.Errorf("dashboard.html is not valid HTML: %v", err)
	}
	err = uploadFile(dashboardPath, "dashboard.html", dashboardBucket)
	if err != nil {
		return errors.Errorf("Unable to upload latest version of "+
			"dashboard HTML: %v", err)
	}

	err = uploadFile(dashboardPath, "dashboard.js", dashboardBucket)
	if err != nil {
		return errors.Errorf("Unable to upload latest version of "+
			"dashboard JS: %v", err)
	}

	productionVulnerabilities, getVulnErr := GetAllVulnerabilities(vulnProject)
	if getVulnErr != nil {
		return errors.Wrap(getVulnErr, "getting all vulnerabilities")
	}

	vulnBreakdowns := GenerateVulnerabilityBreakdown(productionVulnerabilities)
	jsonFile, err := json.MarshalIndent(vulnBreakdowns, "", " ")
	if err != nil {
		return errors.Errorf("Unable to generate dashboard json: %v", err)
	}

	err = ioutil.WriteFile(dashboardPath+"dashboard.json",
		jsonFile, os.ModeTemporary)
	if err != nil {
		return errors.Errorf("Unable to create temporary local"+
			"JSON file for the dashboard: %v", err)
	}
	err = uploadFile(dashboardPath, "dashboard.json", dashboardBucket)
	if err != nil {
		return errors.Errorf("Unable to upload latest version of "+
			"dashboard JSON: %v", err)
	}
	return nil
}
<commit_msg>vulndash: fix file mode when creating the dashboard.json<commit_after>/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package adapter

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"strings"
	"time"

	containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
	"cloud.google.com/go/storage"
	"github.com/pkg/errors"
	"golang.org/x/net/html"
	"google.golang.org/api/iterator"
	grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
)

func uploadFile(directory, filename, bucket string) error {
	const timeout = 60
	ctx := context.Background()
	client, err := storage.NewClient(ctx)
	if err != nil {
		return errors.Errorf("storage.NewClient: %v", err)
	}
	defer client.Close()

	// Open local file
	f, err := os.Open(directory + filename)
	if err != nil {
		return errors.Errorf("os.Open: %v", err)
	}
	defer f.Close()

	ctx, cancel := context.WithTimeout(ctx, time.Second*timeout)
	defer cancel()

	// Upload the object with storage.Writer
	wc := client.Bucket(bucket).Object(filename).NewWriter(ctx)
	if _, err = io.Copy(wc, f); err != nil {
		return errors.Errorf("io.Copy: %v", err)
	}

	if err := wc.Close(); err != nil {
		return errors.Errorf("Writer.Close: %v", err)
	}

	return nil
}

// GetAllVulnerabilities gets all of the vulnerability occurrences associated
// with images in a specific project using the Container Analysis Service.
func GetAllVulnerabilities(
	projectID string,
) ([]*grafeaspb.Occurrence, error) {
	ctx := context.Background()
	client, err := containeranalysis.NewClient(ctx)
	if err != nil {
		return nil, errors.Errorf("NewClient: %v", err)
	}
	defer client.Close()

	req := &grafeaspb.ListOccurrencesRequest{
		Parent: fmt.Sprintf("projects/%s", projectID),
		Filter: fmt.Sprintf("kind = %q", "VULNERABILITY"),
	}

	var occurrenceList []*grafeaspb.Occurrence
	it := client.GetGrafeasClient().ListOccurrences(ctx, req)
	for {
		occ, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, errors.Errorf("occurrence iteration error: %v", err)
		}
		occurrenceList = append(occurrenceList, occ)
	}

	return occurrenceList, err
}

func parseImageResourceURL(resourceURL string) (registryImageName, digest string) {
	FQIN := path.Base(resourceURL)
	splitFQIN := strings.Split(FQIN, "@")
	registryImageName, digest = splitFQIN[0], splitFQIN[1]

	return registryImageName, digest
}

func parseVulnName(noteName string) string {
	return path.Base(noteName)
}

// GenerateVulnerabilityBreakdown parses the a slice of vulnerability
// occurrences into a breakdown that only contains the necessary information
// for each vulnerability.
func GenerateVulnerabilityBreakdown(
	productionVulnerabilities []*grafeaspb.Occurrence,
) map[string]ImageVulnBreakdown {
	vulnBreakdowns := make(map[string]ImageVulnBreakdown)

	for _, occ := range productionVulnerabilities {
		// resourceURI is a url pointing to a specific image
		// in the form gcr.io/project/foo@sha256:111
		if _, found := vulnBreakdowns[occ.ResourceUri]; !found {
			imageName, imageDigest := parseImageResourceURL(occ.ResourceUri)
			vulnBreakdowns[occ.ResourceUri] = ImageVulnBreakdown{
				occ.ResourceUri,
				imageName,
				imageDigest,
				0,
				[]string{},
				[]string{},
			}
		}

		imageVulnBreakdown := vulnBreakdowns[occ.ResourceUri]
		imageVulnBreakdown.NumVulnerabilities++

		vulnName := parseVulnName(occ.NoteName)
		vuln := occ.GetVulnerability()
		if vuln.GetSeverity() == grafeaspb.Severity_CRITICAL {
			imageVulnBreakdown.CriticalVulnerabilities = append(
				imageVulnBreakdown.CriticalVulnerabilities,
				vulnName,
			)
		}
		if vuln.GetFixAvailable() {
			imageVulnBreakdown.FixableVulnerabilities = append(
				imageVulnBreakdown.FixableVulnerabilities,
				vulnName,
			)
		}
		vulnBreakdowns[occ.ResourceUri] = imageVulnBreakdown
	}

	return vulnBreakdowns
}

// UpdateVulnerabilityDashboard updates the vulnerability dashboard by uploading
// the lastest versions of all the vulnerability dashboard's files.
func UpdateVulnerabilityDashboard(
	dashboardPath string,
	vulnProject string,
	dashboardBucket string,
) error {
	htmlReader, openErr := os.Open(dashboardPath + "dashboard.html")
	if openErr != nil {
		return errors.Wrap(openErr, "opening dashboard file")
	}

	_, err := html.Parse(htmlReader)
	if err != nil {
		return errors.Errorf("dashboard.html is not valid HTML: %v", err)
	}
	err = uploadFile(dashboardPath, "dashboard.html", dashboardBucket)
	if err != nil {
		return errors.Errorf("Unable to upload latest version of "+
			"dashboard HTML: %v", err)
	}

	err = uploadFile(dashboardPath, "dashboard.js", dashboardBucket)
	if err != nil {
		return errors.Errorf("Unable to upload latest version of "+
			"dashboard JS: %v", err)
	}

	productionVulnerabilities, getVulnErr := GetAllVulnerabilities(vulnProject)
	if getVulnErr != nil {
		return errors.Wrap(getVulnErr, "getting all vulnerabilities")
	}

	vulnBreakdowns := GenerateVulnerabilityBreakdown(productionVulnerabilities)
	jsonFile, err := json.MarshalIndent(vulnBreakdowns, "", " ")
	if err != nil {
		return errors.Errorf("Unable to generate dashboard json: %v", err)
	}

	err = ioutil.WriteFile(dashboardPath+"dashboard.json",
		jsonFile, 0644)
	if err != nil {
		return errors.Errorf("Unable to create temporary local"+
			"JSON file for the dashboard: %v", err)
	}

	err = uploadFile(dashboardPath, "dashboard.json", dashboardBucket)
	if err != nil {
		return errors.Errorf("Unable to upload latest version of "+
			"dashboard JSON: %v", err)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/onsi/ginkgo/v2"
	"gopkg.in/yaml.v2"

	// Never, ever remove the line with "/ginkgo". Without it,
	// the ginkgo test runner will not detect that this
	// directory contains a Ginkgo test suite.
	// See https://github.com/kubernetes/kubernetes/issues/74827
	// "github.com/onsi/ginkgo/v2"

	"k8s.io/component-base/version"
	"k8s.io/klog/v2"
	conformancetestdata "k8s.io/kubernetes/test/conformance/testdata"
	"k8s.io/kubernetes/test/e2e/framework"
	"k8s.io/kubernetes/test/e2e/framework/config"
	"k8s.io/kubernetes/test/e2e/framework/testfiles"
	e2etestingmanifests "k8s.io/kubernetes/test/e2e/testing-manifests"
	testfixtures "k8s.io/kubernetes/test/fixtures"
	"k8s.io/kubernetes/test/utils/image"

	// test sources
	_ "k8s.io/kubernetes/test/e2e/apimachinery"
	_ "k8s.io/kubernetes/test/e2e/apps"
	_ "k8s.io/kubernetes/test/e2e/architecture"
	_ "k8s.io/kubernetes/test/e2e/auth"
	_ "k8s.io/kubernetes/test/e2e/autoscaling"
	_ "k8s.io/kubernetes/test/e2e/cloud"
	_ "k8s.io/kubernetes/test/e2e/common"
	_ "k8s.io/kubernetes/test/e2e/instrumentation"
	_ "k8s.io/kubernetes/test/e2e/kubectl"
	_ "k8s.io/kubernetes/test/e2e/lifecycle"
	_ "k8s.io/kubernetes/test/e2e/lifecycle/bootstrap"
	_ "k8s.io/kubernetes/test/e2e/network"
	_ "k8s.io/kubernetes/test/e2e/node"
	_ "k8s.io/kubernetes/test/e2e/scheduling"
	_ "k8s.io/kubernetes/test/e2e/storage"
	_ "k8s.io/kubernetes/test/e2e/storage/external"
	_ "k8s.io/kubernetes/test/e2e/windows"
)

// handleFlags sets up all flags and parses the command line.
func handleFlags() {
	config.CopyFlags(config.Flags, flag.CommandLine)
	framework.RegisterCommonFlags(flag.CommandLine)
	framework.RegisterClusterFlags(flag.CommandLine)
	flag.Parse()
}

func TestMain(m *testing.M) {
	var versionFlag bool
	flag.CommandLine.BoolVar(&versionFlag, "version", false, "Displays version information.")

	// Register test flags, then parse flags.
	handleFlags()

	if framework.TestContext.ListImages {
		for _, v := range image.GetImageConfigs() {
			fmt.Println(v.GetE2EImage())
		}
		os.Exit(0)
	}
	if versionFlag {
		fmt.Printf("%s\n", version.Get())
		os.Exit(0)
	}

	// Enable embedded FS file lookup as fallback
	testfiles.AddFileSource(e2etestingmanifests.GetE2ETestingManifestsFS())
	testfiles.AddFileSource(testfixtures.GetTestFixturesFS())
	testfiles.AddFileSource(conformancetestdata.GetConformanceTestdataFS())

	if framework.TestContext.ListConformanceTests {
		var tests []struct {
			Testname    string `yaml:"testname"`
			Codename    string `yaml:"codename"`
			Description string `yaml:"description"`
			Release     string `yaml:"release"`
			File        string `yaml:"file"`
		}

		data, err := testfiles.Read("test/conformance/testdata/conformance.yaml")
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		if err := yaml.Unmarshal(data, &tests); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		if err := yaml.NewEncoder(os.Stdout).Encode(tests); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		os.Exit(0)
	}

	framework.AfterReadingAllFlags(&framework.TestContext)

	// TODO: Deprecating repo-root over time... instead just use gobindata_util.go , see #23987.
	// Right now it is still needed, for example by
	// test/e2e/framework/ingress/ingress_utils.go
	// for providing the optional secret.yaml file and by
	// test/e2e/framework/util.go for cluster/log-dump.
	if framework.TestContext.RepoRoot != "" {
		testfiles.AddFileSource(testfiles.RootFileSource{Root: framework.TestContext.RepoRoot})
	}

	rand.Seed(time.Now().UnixNano())
	os.Exit(m.Run())
}

func TestE2E(t *testing.T) {
	RunE2ETests(t)
}

var _ = ginkgo.ReportAfterEach(func(report ginkgo.SpecReport) {
	progressReporter.ProcessSpecReport(report)
})

var _ = ginkgo.ReportAfterSuite("Kubernetes e2e suite report", func(report ginkgo.Report) {
	var err error
	// The DetailsRepoerter will output details about every test (name, files, lines, etc) which helps
	// when documenting our tests.
	if len(framework.TestContext.SpecSummaryOutput) <= 0 {
		return
	}
	absPath, err := filepath.Abs(framework.TestContext.SpecSummaryOutput)
	if err != nil {
		klog.Errorf("%#v\n", err)
		panic(err)
	}
	f, err := os.Create(absPath)
	if err != nil {
		klog.Errorf("%#v\n", err)
		panic(err)
	}

	defer f.Close()

	for _, specReport := range report.SpecReports {
		b, err := specReport.MarshalJSON()
		if err != nil {
			klog.Errorf("Error in detail reporter: %v", err)
			return
		}
		_, err = f.Write(b)
		if err != nil {
			klog.Errorf("Error saving test details in detail reporter: %v", err)
			return
		}
		// Printing newline between records for easier viewing in various tools.
		_, err = fmt.Fprintln(f, "")
		if err != nil {
			klog.Errorf("Error saving test details in detail reporter: %v", err)
			return
		}
	}
})
<commit_msg>e2e: suppress progress messages for custom progress reporter<commit_after>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/onsi/ginkgo/v2"
	"gopkg.in/yaml.v2"

	// Never, ever remove the line with "/ginkgo". Without it,
	// the ginkgo test runner will not detect that this
	// directory contains a Ginkgo test suite.
	// See https://github.com/kubernetes/kubernetes/issues/74827
	// "github.com/onsi/ginkgo/v2"

	"k8s.io/component-base/version"
	"k8s.io/klog/v2"
	conformancetestdata "k8s.io/kubernetes/test/conformance/testdata"
	"k8s.io/kubernetes/test/e2e/framework"
	"k8s.io/kubernetes/test/e2e/framework/config"
	"k8s.io/kubernetes/test/e2e/framework/testfiles"
	e2etestingmanifests "k8s.io/kubernetes/test/e2e/testing-manifests"
	testfixtures "k8s.io/kubernetes/test/fixtures"
	"k8s.io/kubernetes/test/utils/image"

	// test sources
	_ "k8s.io/kubernetes/test/e2e/apimachinery"
	_ "k8s.io/kubernetes/test/e2e/apps"
	_ "k8s.io/kubernetes/test/e2e/architecture"
	_ "k8s.io/kubernetes/test/e2e/auth"
	_ "k8s.io/kubernetes/test/e2e/autoscaling"
	_ "k8s.io/kubernetes/test/e2e/cloud"
	_ "k8s.io/kubernetes/test/e2e/common"
	_ "k8s.io/kubernetes/test/e2e/instrumentation"
	_ "k8s.io/kubernetes/test/e2e/kubectl"
	_ "k8s.io/kubernetes/test/e2e/lifecycle"
	_ "k8s.io/kubernetes/test/e2e/lifecycle/bootstrap"
	_ "k8s.io/kubernetes/test/e2e/network"
	_ "k8s.io/kubernetes/test/e2e/node"
	_ "k8s.io/kubernetes/test/e2e/scheduling"
	_ "k8s.io/kubernetes/test/e2e/storage"
	_ "k8s.io/kubernetes/test/e2e/storage/external"
	_ "k8s.io/kubernetes/test/e2e/windows"
)

// handleFlags sets up all flags and parses the command line.
func handleFlags() {
	config.CopyFlags(config.Flags, flag.CommandLine)
	framework.RegisterCommonFlags(flag.CommandLine)
	framework.RegisterClusterFlags(flag.CommandLine)
	flag.Parse()
}

func TestMain(m *testing.M) {
	var versionFlag bool
	flag.CommandLine.BoolVar(&versionFlag, "version", false, "Displays version information.")

	// Register test flags, then parse flags.
	handleFlags()

	if framework.TestContext.ListImages {
		for _, v := range image.GetImageConfigs() {
			fmt.Println(v.GetE2EImage())
		}
		os.Exit(0)
	}
	if versionFlag {
		fmt.Printf("%s\n", version.Get())
		os.Exit(0)
	}

	// Enable embedded FS file lookup as fallback
	testfiles.AddFileSource(e2etestingmanifests.GetE2ETestingManifestsFS())
	testfiles.AddFileSource(testfixtures.GetTestFixturesFS())
	testfiles.AddFileSource(conformancetestdata.GetConformanceTestdataFS())

	if framework.TestContext.ListConformanceTests {
		var tests []struct {
			Testname    string `yaml:"testname"`
			Codename    string `yaml:"codename"`
			Description string `yaml:"description"`
			Release     string `yaml:"release"`
			File        string `yaml:"file"`
		}

		data, err := testfiles.Read("test/conformance/testdata/conformance.yaml")
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		if err := yaml.Unmarshal(data, &tests); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		if err := yaml.NewEncoder(os.Stdout).Encode(tests); err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
		os.Exit(0)
	}

	framework.AfterReadingAllFlags(&framework.TestContext)

	// TODO: Deprecating repo-root over time... instead just use gobindata_util.go , see #23987.
	// Right now it is still needed, for example by
	// test/e2e/framework/ingress/ingress_utils.go
	// for providing the optional secret.yaml file and by
	// test/e2e/framework/util.go for cluster/log-dump.
	if framework.TestContext.RepoRoot != "" {
		testfiles.AddFileSource(testfiles.RootFileSource{Root: framework.TestContext.RepoRoot})
	}

	rand.Seed(time.Now().UnixNano())
	os.Exit(m.Run())
}

func TestE2E(t *testing.T) {
	RunE2ETests(t)
}

var _ = ginkgo.ReportAfterEach(func(report ginkgo.SpecReport) {
	progressReporter.ProcessSpecReport(report)
}, ginkgo.SuppressProgressReporting)

var _ = ginkgo.ReportAfterSuite("Kubernetes e2e suite report", func(report ginkgo.Report) {
	var err error
	// The DetailsRepoerter will output details about every test (name, files, lines, etc) which helps
	// when documenting our tests.
	if len(framework.TestContext.SpecSummaryOutput) <= 0 {
		return
	}
	absPath, err := filepath.Abs(framework.TestContext.SpecSummaryOutput)
	if err != nil {
		klog.Errorf("%#v\n", err)
		panic(err)
	}
	f, err := os.Create(absPath)
	if err != nil {
		klog.Errorf("%#v\n", err)
		panic(err)
	}

	defer f.Close()

	for _, specReport := range report.SpecReports {
		b, err := specReport.MarshalJSON()
		if err != nil {
			klog.Errorf("Error in detail reporter: %v", err)
			return
		}
		_, err = f.Write(b)
		if err != nil {
			klog.Errorf("Error saving test details in detail reporter: %v", err)
			return
		}
		// Printing newline between records for easier viewing in various tools.
		_, err = fmt.Fprintln(f, "")
		if err != nil {
			klog.Errorf("Error saving test details in detail reporter: %v", err)
			return
		}
	}
})
<|endoftext|>


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

package linux

// PR_* flags, from <linux/pcrtl.h> for prctl(2).
const (
	// PR_SET_PDEATHSIG will set the process' death signal.
	PR_SET_PDEATHSIG = 1

	// PR_GET_PDEATHSIG will get the process' death signal.
	PR_GET_PDEATHSIG = 2

	// PR_GET_DUMPABLE will get the process's dumpable flag.
	PR_GET_DUMPABLE = 3

	// PR_SET_DUMPABLE will set the process's dumpable flag.
	PR_SET_DUMPABLE = 4

	// PR_GET_KEEPCAPS will get the value of the keep capabilities flag.
	PR_GET_KEEPCAPS = 7

	// PR_SET_KEEPCAPS will set the value of the keep capabilities flag.
	PR_SET_KEEPCAPS = 8

	// PR_GET_TIMING will get the process's timing method.
	PR_GET_TIMING = 13

	// PR_SET_TIMING will set the process's timing method.
	PR_SET_TIMING = 14

	// PR_SET_NAME will set the process' name.
	PR_SET_NAME = 15

	// PR_GET_NAME will get the process' name.
	PR_GET_NAME = 16

	// PR_GET_SECCOMP will get a process' seccomp mode.
	PR_GET_SECCOMP = 21

	// PR_SET_SECCOMP will set a process' seccomp mode.
	PR_SET_SECCOMP = 22

	// PR_CAPBSET_READ will get the capability bounding set.
	PR_CAPBSET_READ = 23

	// PR_CAPBSET_DROP will set the capability bounding set.
	PR_CAPBSET_DROP = 24

	// PR_GET_TSC will get the the value of the flag determining whether the
	// timestamp counter can be read.
	PR_GET_TSC = 25

	// PR_SET_TSC will set the the value of the flag determining whether the
	// timestamp counter can be read.
	PR_SET_TSC = 26

	// PR_SET_TIMERSLACK set the process's time slack.
	PR_SET_TIMERSLACK = 29

	// PR_GET_TIMERSLACK get the process's time slack.
	PR_GET_TIMERSLACK = 30

	// PR_TASK_PERF_EVENTS_DISABLE disable all performance counters attached to
	// the calling process.
	PR_TASK_PERF_EVENTS_DISABLE = 31

	// PR_TASK_PERF_EVENTS_ENABLE enable all performance counters attached to
	// the calling process.
	PR_TASK_PERF_EVENTS_ENABLE = 32

	// PR_MCE_KILL set the machine check memory corruption kill policy for the
	// calling thread.
	PR_MCE_KILL = 33

	// PR_MCE_KILL_GET get the machine check memory corruption kill policy for the
	// calling thread.
	PR_MCE_KILL_GET = 34

	// PR_SET_MM will modify certain kernel memory map descriptor fields of the
	// calling process. See prctl(2) for more information.
	PR_SET_MM = 35

	PR_SET_MM_START_CODE  = 1
	PR_SET_MM_END_CODE    = 2
	PR_SET_MM_START_DATA  = 3
	PR_SET_MM_END_DATA    = 4
	PR_SET_MM_START_STACK = 5
	PR_SET_MM_START_BRK   = 6
	PR_SET_MM_BRK         = 7
	PR_SET_MM_ARG_START   = 8
	PR_SET_MM_ARG_END     = 9
	PR_SET_MM_ENV_START   = 10
	PR_SET_MM_ENV_END     = 11
	PR_SET_MM_AUXV        = 12
	// PR_SET_MM_EXE_FILE will supersede the /proc/pid/exe symbolic link with a
	// new one pointing to a new executable file identified by the file descriptor
	// provided in arg3 argument. See prctl(2) for more information.
	PR_SET_MM_EXE_FILE = 13
	PR_SET_MM_MAP      = 14
	PR_SET_MM_MAP_SIZE = 15

	// PR_SET_CHILD_SUBREAPER set the "child subreaper" attribute of the calling
	// process.
	PR_SET_CHILD_SUBREAPER = 36

	// PR_GET_CHILD_SUBREAPER get the "child subreaper" attribute of the calling
	// process.
	PR_GET_CHILD_SUBREAPER = 37

	// PR_SET_NO_NEW_PRIVS will set the calling thread's no_new_privs bit.
	PR_SET_NO_NEW_PRIVS = 38

	// PR_GET_NO_NEW_PRIVS will get the calling thread's no_new_privs bit.
	PR_GET_NO_NEW_PRIVS = 39

	// PR_GET_TID_ADDRESS retrieve the clear_child_tid address.
	PR_GET_TID_ADDRESS = 40

	// PR_SET_THP_DISABLE set the state of the "THP disable" flag for the calling
	// thread.
	PR_SET_THP_DISABLE = 41

	// PR_GET_THP_DISABLE get the state of the "THP disable" flag for the calling
	// thread.
	PR_GET_THP_DISABLE = 42

	// PR_MPX_ENABLE_MANAGEMENT enable kernel management of Memory Protection
	// eXtensions (MPX) bounds tables.
	PR_MPX_ENABLE_MANAGEMENT = 43

	// PR_MPX_DISABLE_MANAGEMENTdisable kernel management of Memory Protection
	// eXtensions (MPX) bounds tables.
	PR_MPX_DISABLE_MANAGEMENT = 44
)

// From <asm/prctl.h>
// Flags are used in syscall arch_prctl(2).
const (
	ARCH_SET_GS    = 0x1001
	ARCH_SET_FS    = 0x1002
	ARCH_GET_FS    = 0x1003
	ARCH_GET_GS    = 0x1004
	ARCH_SET_CPUID = 0x1012
)
<commit_msg>Wrap comments and reword in common present tense<commit_after>// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package linux

// PR_* flags, from <linux/pcrtl.h> for prctl(2).
const (
	// PR_SET_PDEATHSIG sets the process' death signal.
	PR_SET_PDEATHSIG = 1

	// PR_GET_PDEATHSIG gets the process' death signal.
	PR_GET_PDEATHSIG = 2

	// PR_GET_DUMPABLE gets the process' dumpable flag.
	PR_GET_DUMPABLE = 3

	// PR_SET_DUMPABLE sets the process' dumpable flag.
	PR_SET_DUMPABLE = 4

	// PR_GET_KEEPCAPS gets the value of the keep capabilities flag.
	PR_GET_KEEPCAPS = 7

	// PR_SET_KEEPCAPS sets the value of the keep capabilities flag.
	PR_SET_KEEPCAPS = 8

	// PR_GET_TIMING gets the process' timing method.
	PR_GET_TIMING = 13

	// PR_SET_TIMING sets the process' timing method.
	PR_SET_TIMING = 14

	// PR_SET_NAME sets the process' name.
	PR_SET_NAME = 15

	// PR_GET_NAME gets the process' name.
	PR_GET_NAME = 16

	// PR_GET_SECCOMP gets a process' seccomp mode.
	PR_GET_SECCOMP = 21

	// PR_SET_SECCOMP sets a process' seccomp mode.
	PR_SET_SECCOMP = 22

	// PR_CAPBSET_READ gets the capability bounding set.
	PR_CAPBSET_READ = 23

	// PR_CAPBSET_DROP sets the capability bounding set.
	PR_CAPBSET_DROP = 24

	// PR_GET_TSC gets the value of the flag determining whether the
	// timestamp counter can be read.
	PR_GET_TSC = 25

	// PR_SET_TSC sets the value of the flag determining whether the
	// timestamp counter can be read.
	PR_SET_TSC = 26

	// PR_SET_TIMERSLACK sets the process' time slack.
	PR_SET_TIMERSLACK = 29

	// PR_GET_TIMERSLACK gets the process' time slack.
	PR_GET_TIMERSLACK = 30

	// PR_TASK_PERF_EVENTS_DISABLE disables all performance counters
	// attached to the calling process.
	PR_TASK_PERF_EVENTS_DISABLE = 31

	// PR_TASK_PERF_EVENTS_ENABLE enables all performance counters attached
	// to the calling process.
	PR_TASK_PERF_EVENTS_ENABLE = 32

	// PR_MCE_KILL sets the machine check memory corruption kill policy for
	// the calling thread.
	PR_MCE_KILL = 33

	// PR_MCE_KILL_GET gets the machine check memory corruption kill policy
	// for the calling thread.
	PR_MCE_KILL_GET = 34

	// PR_SET_MM modifies certain kernel memory map descriptor fields of
	// the calling process. See prctl(2) for more information.
	PR_SET_MM = 35

	PR_SET_MM_START_CODE  = 1
	PR_SET_MM_END_CODE    = 2
	PR_SET_MM_START_DATA  = 3
	PR_SET_MM_END_DATA    = 4
	PR_SET_MM_START_STACK = 5
	PR_SET_MM_START_BRK   = 6
	PR_SET_MM_BRK         = 7
	PR_SET_MM_ARG_START   = 8
	PR_SET_MM_ARG_END     = 9
	PR_SET_MM_ENV_START   = 10
	PR_SET_MM_ENV_END     = 11
	PR_SET_MM_AUXV        = 12
	// PR_SET_MM_EXE_FILE supersedes the /proc/pid/exe symbolic link with a
	// new one pointing to a new executable file identified by the file
	// descriptor provided in arg3 argument. See prctl(2) for more
	// information.
	PR_SET_MM_EXE_FILE = 13
	PR_SET_MM_MAP      = 14
	PR_SET_MM_MAP_SIZE = 15

	// PR_SET_CHILD_SUBREAPER sets the "child subreaper" attribute of the
	// calling process.
	PR_SET_CHILD_SUBREAPER = 36

	// PR_GET_CHILD_SUBREAPER gets the "child subreaper" attribute of the
	// calling process.
	PR_GET_CHILD_SUBREAPER = 37

	// PR_SET_NO_NEW_PRIVS sets the calling thread's no_new_privs bit.
	PR_SET_NO_NEW_PRIVS = 38

	// PR_GET_NO_NEW_PRIVS gets the calling thread's no_new_privs bit.
	PR_GET_NO_NEW_PRIVS = 39

	// PR_GET_TID_ADDRESS retrieves the clear_child_tid address.
	PR_GET_TID_ADDRESS = 40

	// PR_SET_THP_DISABLE sets the state of the "THP disable" flag for the
	// calling thread.
	PR_SET_THP_DISABLE = 41

	// PR_GET_THP_DISABLE gets the state of the "THP disable" flag for the
	// calling thread.
	PR_GET_THP_DISABLE = 42

	// PR_MPX_ENABLE_MANAGEMENT enables kernel management of Memory
	// Protection eXtensions (MPX) bounds tables.
	PR_MPX_ENABLE_MANAGEMENT = 43

	// PR_MPX_DISABLE_MANAGEMENT disables kernel management of Memory
	// Protection eXtensions (MPX) bounds tables.
	PR_MPX_DISABLE_MANAGEMENT = 44
)

// From <asm/prctl.h>
// Flags are used in syscall arch_prctl(2).
const (
	ARCH_SET_GS    = 0x1001
	ARCH_SET_FS    = 0x1002
	ARCH_GET_FS    = 0x1003
	ARCH_GET_GS    = 0x1004
	ARCH_SET_CPUID = 0x1012
)
<|endoftext|>


<commit_before>package api

import (
	"context"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"net/http"
	"net/url"

	"golang.org/x/oauth2"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/infra/metrics"
	"github.com/grafana/grafana/pkg/login"
	"github.com/grafana/grafana/pkg/login/social"
	"github.com/grafana/grafana/pkg/middleware/cookies"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/org"
	"github.com/grafana/grafana/pkg/services/user"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/web"
)

var (
	oauthLogger = log.New("oauth")
)

const (
	OauthStateCookieName = "oauth_state"
	OauthPKCECookieName  = "oauth_code_verifier"
)

func GenStateString() (string, error) {
	rnd := make([]byte, 32)
	if _, err := rand.Read(rnd); err != nil {
		oauthLogger.Error("failed to generate state string", "err", err)
		return "", err
	}
	return base64.URLEncoding.EncodeToString(rnd), nil
}

// genPKCECode returns a random URL-friendly string and it's base64 URL encoded SHA256 digest.
func genPKCECode() (string, string, error) {
	// IETF RFC 7636 specifies that the code verifier should be 43-128
	// characters from a set of unreserved URI characters which is
	// almost the same as the set of characters in base64url.
	// https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
	//
	// It doesn't hurt to generate a few more bytes here, we generate
	// 96 bytes which we then encode using base64url to make sure
	// they're within the set of unreserved characters.
	//
	// 96 is chosen because 96*8/6 = 128, which means that we'll have
	// 128 characters after it has been base64 encoded.
	raw := make([]byte, 96)
	_, err := rand.Read(raw)
	if err != nil {
		return "", "", err
	}
	ascii := make([]byte, 128)
	base64.RawURLEncoding.Encode(ascii, raw)

	shasum := sha256.Sum256(ascii)
	pkce := base64.RawURLEncoding.EncodeToString(shasum[:])
	return string(ascii), pkce, nil
}

func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
	loginInfo := models.LoginInfo{
		AuthModule: "oauth",
	}
	name := web.Params(ctx.Req)[":name"]
	loginInfo.AuthModule = name
	provider := hs.SocialService.GetOAuthInfoProvider(name)
	if provider == nil {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, errors.New("OAuth not enabled"))
		return
	}

	connect, err := hs.SocialService.GetConnector(name)
	if err != nil {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, fmt.Errorf("no OAuth with name %s configured", name))
		return
	}

	errorParam := ctx.Query("error")
	if errorParam != "" {
		errorDesc := ctx.Query("error_description")
		oauthLogger.Error("failed to login ", "error", errorParam, "errorDesc", errorDesc)
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrProviderDeniedRequest, "error", errorParam, "errorDesc", errorDesc)
		return
	}

	code := ctx.Query("code")
	if code == "" {
		opts := []oauth2.AuthCodeOption{oauth2.AccessTypeOnline}

		if provider.UsePKCE {
			ascii, pkce, err := genPKCECode()
			if err != nil {
				ctx.Logger.Error("Generating PKCE failed", "error", err)
				hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
					HttpStatus:    http.StatusInternalServerError,
					PublicMessage: "An internal error occurred",
				})
			}

			cookies.WriteCookie(ctx.Resp, OauthPKCECookieName, ascii, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)

			opts = append(opts,
				oauth2.SetAuthURLParam("code_challenge", pkce),
				oauth2.SetAuthURLParam("code_challenge_method", "S256"),
			)
		}

		state, err := GenStateString()
		if err != nil {
			ctx.Logger.Error("Generating state string failed", "err", err)
			hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
				HttpStatus:    http.StatusInternalServerError,
				PublicMessage: "An internal error occurred",
			})
			return
		}

		hashedState := hs.hashStatecode(state, provider.ClientSecret)
		cookies.WriteCookie(ctx.Resp, OauthStateCookieName, hashedState, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
		if provider.HostedDomain != "" {
			opts = append(opts, oauth2.SetAuthURLParam("hd", provider.HostedDomain))
		}

		ctx.Redirect(connect.AuthCodeURL(state, opts...))
		return
	}

	cookieState := ctx.GetCookie(OauthStateCookieName)

	// delete cookie
	cookies.DeleteCookie(ctx.Resp, OauthStateCookieName, hs.CookieOptionsFromCfg)

	if cookieState == "" {
		hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
			HttpStatus:    http.StatusInternalServerError,
			PublicMessage: "login.OAuthLogin(missing saved state)",
		})
		return
	}

	queryState := hs.hashStatecode(ctx.Query("state"), provider.ClientSecret)
	oauthLogger.Info("state check", "queryState", queryState, "cookieState", cookieState)
	if cookieState != queryState {
		hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
			HttpStatus:    http.StatusInternalServerError,
			PublicMessage: "login.OAuthLogin(state mismatch)",
		})
		return
	}

	oauthClient, err := hs.SocialService.GetOAuthHttpClient(name)
	if err != nil {
		ctx.Logger.Error("Failed to create OAuth http client", "error", err)
		hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
			HttpStatus:    http.StatusInternalServerError,
			PublicMessage: "login.OAuthLogin(" + err.Error() + ")",
		})
		return
	}

	oauthCtx := context.WithValue(context.Background(), oauth2.HTTPClient, oauthClient)
	opts := []oauth2.AuthCodeOption{}

	codeVerifier := ctx.GetCookie(OauthPKCECookieName)
	cookies.DeleteCookie(ctx.Resp, OauthPKCECookieName, hs.CookieOptionsFromCfg)
	if codeVerifier != "" {
		opts = append(opts,
			oauth2.SetAuthURLParam("code_verifier", codeVerifier),
		)
	}

	// get token from provider
	token, err := connect.Exchange(oauthCtx, code, opts...)
	if err != nil {
		hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
			HttpStatus:    http.StatusInternalServerError,
			PublicMessage: "login.OAuthLogin(NewTransportWithCode)",
			Err:           err,
		})
		return
	}
	// token.TokenType was defaulting to "bearer", which is out of spec, so we explicitly set to "Bearer"
	token.TokenType = "Bearer"

	oauthLogger.Debug("OAuthLogin: got token", "token", fmt.Sprintf("%+v", token))

	// set up oauth2 client
	client := connect.Client(oauthCtx, token)

	// get user info
	userInfo, err := connect.UserInfo(client, token)
	if err != nil {
		var sErr *social.Error
		if errors.As(err, &sErr) {
			hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, sErr)
		} else {
			hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
				HttpStatus:    http.StatusInternalServerError,
				PublicMessage: fmt.Sprintf("login.OAuthLogin(get info from %s)", name),
				Err:           err,
			})
		}
		return
	}

	oauthLogger.Debug("OAuthLogin got user info", "userInfo", fmt.Sprintf("%v", userInfo))

	// validate that we got at least an email address
	if userInfo.Email == "" {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrNoEmail)
		return
	}

	// validate that the email is allowed to login to grafana
	if !connect.IsEmailAllowed(userInfo.Email) {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrEmailNotAllowed)
		return
	}

	loginInfo.ExternalUser = *hs.buildExternalUserInfo(token, userInfo, name)
	loginInfo.User, err = hs.SyncUser(ctx, &loginInfo.ExternalUser, connect)
	if err != nil {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err)
		return
	}

	// login
	if err := hs.loginUserWithUser(loginInfo.User, ctx); err != nil {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err)
		return
	}

	loginInfo.HTTPStatus = http.StatusOK
	hs.HooksService.RunLoginHook(&loginInfo, ctx)
	metrics.MApiLoginOAuth.Inc()

	if redirectTo, err := url.QueryUnescape(ctx.GetCookie("redirect_to")); err == nil && len(redirectTo) > 0 {
		if err := hs.ValidateRedirectTo(redirectTo); err == nil {
			cookies.DeleteCookie(ctx.Resp, "redirect_to", hs.CookieOptionsFromCfg)
			ctx.Redirect(redirectTo)
			return
		}
		ctx.Logger.Debug("Ignored invalid redirect_to cookie value", "redirect_to", redirectTo)
	}

	ctx.Redirect(setting.AppSubUrl + "/")
}

// buildExternalUserInfo returns a ExternalUserInfo struct from OAuth user profile
func (hs *HTTPServer) buildExternalUserInfo(token *oauth2.Token, userInfo *social.BasicUserInfo, name string) *models.ExternalUserInfo {
	oauthLogger.Debug("Building external user info from OAuth user info")

	extUser := &models.ExternalUserInfo{
		AuthModule:     fmt.Sprintf("oauth_%s", name),
		OAuthToken:     token,
		AuthId:         userInfo.Id,
		Name:           userInfo.Name,
		Login:          userInfo.Login,
		Email:          userInfo.Email,
		OrgRoles:       map[int64]org.RoleType{},
		Groups:         userInfo.Groups,
		IsGrafanaAdmin: userInfo.IsGrafanaAdmin,
	}

	if userInfo.Role != "" && !hs.Cfg.OAuthSkipOrgRoleUpdateSync {
		rt := userInfo.Role
		if rt.IsValid() {
			// The user will be assigned a role in either the auto-assigned organization or in the default one
			var orgID int64
			if hs.Cfg.AutoAssignOrg && hs.Cfg.AutoAssignOrgId > 0 {
				orgID = int64(hs.Cfg.AutoAssignOrgId)
				plog.Debug("The user has a role assignment and organization membership is auto-assigned",
					"role", userInfo.Role, "orgId", orgID)
			} else {
				orgID = int64(1)
				plog.Debug("The user has a role assignment and organization membership is not auto-assigned",
					"role", userInfo.Role, "orgId", orgID)
			}
			extUser.OrgRoles[orgID] = rt
		}
	}

	return extUser
}

// SyncUser syncs a Grafana user profile with the corresponding OAuth profile.
func (hs *HTTPServer) SyncUser(
	ctx *models.ReqContext,
	extUser *models.ExternalUserInfo,
	connect social.SocialConnector,
) (*user.User, error) {
	oauthLogger.Debug("Syncing Grafana user with corresponding OAuth profile")
	// add/update user in Grafana
	cmd := &models.UpsertUserCommand{
		ReqContext:    ctx,
		ExternalUser:  extUser,
		SignupAllowed: connect.IsSignupAllowed(),
		UserLookupParams: models.UserLookupParams{
			Email:  &extUser.Email,
			UserID: nil,
			Login:  nil,
		},
	}

	if err := hs.Login.UpsertUser(ctx.Req.Context(), cmd); err != nil {
		return nil, err
	}

	// Do not expose disabled status,
	// just show incorrect user credentials error (see #17947)
	if cmd.Result.IsDisabled {
		oauthLogger.Warn("User is disabled", "user", cmd.Result.Login)
		return nil, login.ErrInvalidCredentials
	}

	return cmd.Result, nil
}

func (hs *HTTPServer) hashStatecode(code, seed string) string {
	hashBytes := sha256.Sum256([]byte(code + hs.Cfg.SecretKey + seed))
	return hex.EncodeToString(hashBytes[:])
}

type LoginError struct {
	HttpStatus    int
	PublicMessage string
	Err           error
}

func (hs *HTTPServer) handleOAuthLoginError(ctx *models.ReqContext, info models.LoginInfo, err LoginError) {
	ctx.Handle(hs.Cfg, err.HttpStatus, err.PublicMessage, err.Err)

	info.Error = err.Err
	if info.Error == nil {
		info.Error = errors.New(err.PublicMessage)
	}
	info.HTTPStatus = err.HttpStatus

	hs.HooksService.RunLoginHook(&info, ctx)
}

func (hs *HTTPServer) handleOAuthLoginErrorWithRedirect(ctx *models.ReqContext, info models.LoginInfo, err error, v ...interface{}) {
	hs.redirectWithError(ctx, err, v...)

	info.Error = err
	hs.HooksService.RunLoginHook(&info, ctx)
}
<commit_msg>Exclude full OAuth token details from printing out on stdout (#55426)<commit_after>package api

import (
	"context"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"net/http"
	"net/url"

	"golang.org/x/oauth2"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/infra/metrics"
	"github.com/grafana/grafana/pkg/login"
	"github.com/grafana/grafana/pkg/login/social"
	"github.com/grafana/grafana/pkg/middleware/cookies"
	"github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/org"
	"github.com/grafana/grafana/pkg/services/user"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/web"
)

var (
	oauthLogger = log.New("oauth")
)

const (
	OauthStateCookieName = "oauth_state"
	OauthPKCECookieName  = "oauth_code_verifier"
)

func GenStateString() (string, error) {
	rnd := make([]byte, 32)
	if _, err := rand.Read(rnd); err != nil {
		oauthLogger.Error("failed to generate state string", "err", err)
		return "", err
	}
	return base64.URLEncoding.EncodeToString(rnd), nil
}

// genPKCECode returns a random URL-friendly string and it's base64 URL encoded SHA256 digest.
func genPKCECode() (string, string, error) {
	// IETF RFC 7636 specifies that the code verifier should be 43-128
	// characters from a set of unreserved URI characters which is
	// almost the same as the set of characters in base64url.
	// https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
	//
	// It doesn't hurt to generate a few more bytes here, we generate
	// 96 bytes which we then encode using base64url to make sure
	// they're within the set of unreserved characters.
	//
	// 96 is chosen because 96*8/6 = 128, which means that we'll have
	// 128 characters after it has been base64 encoded.
	raw := make([]byte, 96)
	_, err := rand.Read(raw)
	if err != nil {
		return "", "", err
	}
	ascii := make([]byte, 128)
	base64.RawURLEncoding.Encode(ascii, raw)

	shasum := sha256.Sum256(ascii)
	pkce := base64.RawURLEncoding.EncodeToString(shasum[:])
	return string(ascii), pkce, nil
}

func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
	loginInfo := models.LoginInfo{
		AuthModule: "oauth",
	}
	name := web.Params(ctx.Req)[":name"]
	loginInfo.AuthModule = name
	provider := hs.SocialService.GetOAuthInfoProvider(name)
	if provider == nil {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, errors.New("OAuth not enabled"))
		return
	}

	connect, err := hs.SocialService.GetConnector(name)
	if err != nil {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, fmt.Errorf("no OAuth with name %s configured", name))
		return
	}

	errorParam := ctx.Query("error")
	if errorParam != "" {
		errorDesc := ctx.Query("error_description")
		oauthLogger.Error("failed to login ", "error", errorParam, "errorDesc", errorDesc)
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrProviderDeniedRequest, "error", errorParam, "errorDesc", errorDesc)
		return
	}

	code := ctx.Query("code")
	if code == "" {
		opts := []oauth2.AuthCodeOption{oauth2.AccessTypeOnline}

		if provider.UsePKCE {
			ascii, pkce, err := genPKCECode()
			if err != nil {
				ctx.Logger.Error("Generating PKCE failed", "error", err)
				hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
					HttpStatus:    http.StatusInternalServerError,
					PublicMessage: "An internal error occurred",
				})
			}

			cookies.WriteCookie(ctx.Resp, OauthPKCECookieName, ascii, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)

			opts = append(opts,
				oauth2.SetAuthURLParam("code_challenge", pkce),
				oauth2.SetAuthURLParam("code_challenge_method", "S256"),
			)
		}

		state, err := GenStateString()
		if err != nil {
			ctx.Logger.Error("Generating state string failed", "err", err)
			hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
				HttpStatus:    http.StatusInternalServerError,
				PublicMessage: "An internal error occurred",
			})
			return
		}

		hashedState := hs.hashStatecode(state, provider.ClientSecret)
		cookies.WriteCookie(ctx.Resp, OauthStateCookieName, hashedState, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg)
		if provider.HostedDomain != "" {
			opts = append(opts, oauth2.SetAuthURLParam("hd", provider.HostedDomain))
		}

		ctx.Redirect(connect.AuthCodeURL(state, opts...))
		return
	}

	cookieState := ctx.GetCookie(OauthStateCookieName)

	// delete cookie
	cookies.DeleteCookie(ctx.Resp, OauthStateCookieName, hs.CookieOptionsFromCfg)

	if cookieState == "" {
		hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
			HttpStatus:    http.StatusInternalServerError,
			PublicMessage: "login.OAuthLogin(missing saved state)",
		})
		return
	}

	queryState := hs.hashStatecode(ctx.Query("state"), provider.ClientSecret)
	oauthLogger.Info("state check", "queryState", queryState, "cookieState", cookieState)
	if cookieState != queryState {
		hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
			HttpStatus:    http.StatusInternalServerError,
			PublicMessage: "login.OAuthLogin(state mismatch)",
		})
		return
	}

	oauthClient, err := hs.SocialService.GetOAuthHttpClient(name)
	if err != nil {
		ctx.Logger.Error("Failed to create OAuth http client", "error", err)
		hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
			HttpStatus:    http.StatusInternalServerError,
			PublicMessage: "login.OAuthLogin(" + err.Error() + ")",
		})
		return
	}

	oauthCtx := context.WithValue(context.Background(), oauth2.HTTPClient, oauthClient)
	opts := []oauth2.AuthCodeOption{}

	codeVerifier := ctx.GetCookie(OauthPKCECookieName)
	cookies.DeleteCookie(ctx.Resp, OauthPKCECookieName, hs.CookieOptionsFromCfg)
	if codeVerifier != "" {
		opts = append(opts,
			oauth2.SetAuthURLParam("code_verifier", codeVerifier),
		)
	}

	// get token from provider
	token, err := connect.Exchange(oauthCtx, code, opts...)
	if err != nil {
		hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
			HttpStatus:    http.StatusInternalServerError,
			PublicMessage: "login.OAuthLogin(NewTransportWithCode)",
			Err:           err,
		})
		return
	}
	// token.TokenType was defaulting to "bearer", which is out of spec, so we explicitly set to "Bearer"
	token.TokenType = "Bearer"

	oauthLogger.Debug("OAuthLogin: got token", "expiry", fmt.Sprintf("%v", token.Expiry))

	// set up oauth2 client
	client := connect.Client(oauthCtx, token)

	// get user info
	userInfo, err := connect.UserInfo(client, token)
	if err != nil {
		var sErr *social.Error
		if errors.As(err, &sErr) {
			hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, sErr)
		} else {
			hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
				HttpStatus:    http.StatusInternalServerError,
				PublicMessage: fmt.Sprintf("login.OAuthLogin(get info from %s)", name),
				Err:           err,
			})
		}
		return
	}

	oauthLogger.Debug("OAuthLogin got user info", "userInfo", fmt.Sprintf("%v", userInfo))

	// validate that we got at least an email address
	if userInfo.Email == "" {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrNoEmail)
		return
	}

	// validate that the email is allowed to login to grafana
	if !connect.IsEmailAllowed(userInfo.Email) {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, login.ErrEmailNotAllowed)
		return
	}

	loginInfo.ExternalUser = *hs.buildExternalUserInfo(token, userInfo, name)
	loginInfo.User, err = hs.SyncUser(ctx, &loginInfo.ExternalUser, connect)
	if err != nil {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err)
		return
	}

	// login
	if err := hs.loginUserWithUser(loginInfo.User, ctx); err != nil {
		hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err)
		return
	}

	loginInfo.HTTPStatus = http.StatusOK
	hs.HooksService.RunLoginHook(&loginInfo, ctx)
	metrics.MApiLoginOAuth.Inc()

	if redirectTo, err := url.QueryUnescape(ctx.GetCookie("redirect_to")); err == nil && len(redirectTo) > 0 {
		if err := hs.ValidateRedirectTo(redirectTo); err == nil {
			cookies.DeleteCookie(ctx.Resp, "redirect_to", hs.CookieOptionsFromCfg)
			ctx.Redirect(redirectTo)
			return
		}
		ctx.Logger.Debug("Ignored invalid redirect_to cookie value", "redirect_to", redirectTo)
	}

	ctx.Redirect(setting.AppSubUrl + "/")
}

// buildExternalUserInfo returns a ExternalUserInfo struct from OAuth user profile
func (hs *HTTPServer) buildExternalUserInfo(token *oauth2.Token, userInfo *social.BasicUserInfo, name string) *models.ExternalUserInfo {
	oauthLogger.Debug("Building external user info from OAuth user info")

	extUser := &models.ExternalUserInfo{
		AuthModule:     fmt.Sprintf("oauth_%s", name),
		OAuthToken:     token,
		AuthId:         userInfo.Id,
		Name:           userInfo.Name,
		Login:          userInfo.Login,
		Email:          userInfo.Email,
		OrgRoles:       map[int64]org.RoleType{},
		Groups:         userInfo.Groups,
		IsGrafanaAdmin: userInfo.IsGrafanaAdmin,
	}

	if userInfo.Role != "" && !hs.Cfg.OAuthSkipOrgRoleUpdateSync {
		rt := userInfo.Role
		if rt.IsValid() {
			// The user will be assigned a role in either the auto-assigned organization or in the default one
			var orgID int64
			if hs.Cfg.AutoAssignOrg && hs.Cfg.AutoAssignOrgId > 0 {
				orgID = int64(hs.Cfg.AutoAssignOrgId)
				plog.Debug("The user has a role assignment and organization membership is auto-assigned",
					"role", userInfo.Role, "orgId", orgID)
			} else {
				orgID = int64(1)
				plog.Debug("The user has a role assignment and organization membership is not auto-assigned",
					"role", userInfo.Role, "orgId", orgID)
			}
			extUser.OrgRoles[orgID] = rt
		}
	}

	return extUser
}

// SyncUser syncs a Grafana user profile with the corresponding OAuth profile.
func (hs *HTTPServer) SyncUser(
	ctx *models.ReqContext,
	extUser *models.ExternalUserInfo,
	connect social.SocialConnector,
) (*user.User, error) {
	oauthLogger.Debug("Syncing Grafana user with corresponding OAuth profile")
	// add/update user in Grafana
	cmd := &models.UpsertUserCommand{
		ReqContext:    ctx,
		ExternalUser:  extUser,
		SignupAllowed: connect.IsSignupAllowed(),
		UserLookupParams: models.UserLookupParams{
			Email:  &extUser.Email,
			UserID: nil,
			Login:  nil,
		},
	}

	if err := hs.Login.UpsertUser(ctx.Req.Context(), cmd); err != nil {
		return nil, err
	}

	// Do not expose disabled status,
	// just show incorrect user credentials error (see #17947)
	if cmd.Result.IsDisabled {
		oauthLogger.Warn("User is disabled", "user", cmd.Result.Login)
		return nil, login.ErrInvalidCredentials
	}

	return cmd.Result, nil
}

func (hs *HTTPServer) hashStatecode(code, seed string) string {
	hashBytes := sha256.Sum256([]byte(code + hs.Cfg.SecretKey + seed))
	return hex.EncodeToString(hashBytes[:])
}

type LoginError struct {
	HttpStatus    int
	PublicMessage string
	Err           error
}

func (hs *HTTPServer) handleOAuthLoginError(ctx *models.ReqContext, info models.LoginInfo, err LoginError) {
	ctx.Handle(hs.Cfg, err.HttpStatus, err.PublicMessage, err.Err)

	info.Error = err.Err
	if info.Error == nil {
		info.Error = errors.New(err.PublicMessage)
	}
	info.HTTPStatus = err.HttpStatus

	hs.HooksService.RunLoginHook(&info, ctx)
}

func (hs *HTTPServer) handleOAuthLoginErrorWithRedirect(ctx *models.ReqContext, info models.LoginInfo, err error, v ...interface{}) {
	hs.redirectWithError(ctx, err, v...)

	info.Error = err
	hs.HooksService.RunLoginHook(&info, ctx)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 chartutil

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"path"
	"strings"

	"github.com/ghodss/yaml"

	"github.com/BurntSushi/toml"
	"github.com/gobwas/glob"
	"github.com/golang/protobuf/ptypes/any"
)

// Files is a map of files in a chart that can be accessed from a template.
type Files map[string][]byte

// NewFiles creates a new Files from chart files.
// Given an []*any.Any (the format for files in a chart.Chart), extract a map of files.
func NewFiles(from []*any.Any) Files {
	files := map[string][]byte{}
	if from != nil {
		for _, f := range from {
			files[f.TypeUrl] = f.Value
		}
	}
	return files
}

// GetBytes gets a file by path.
//
// The returned data is raw. In a template context, this is identical to calling
// {{index .Files $path}}.
//
// This is intended to be accessed from within a template, so a missed key returns
// an empty []byte.
func (f Files) GetBytes(name string) []byte {
	v, ok := f[name]
	if !ok {
		return []byte{}
	}
	return v
}

// Get returns a string representation of the given file.
//
// Fetch the contents of a file as a string. It is designed to be called in a
// template.
//
//	{{.Files.Get "foo"}}
func (f Files) Get(name string) string {
	return string(f.GetBytes(name))
}

// Glob takes a glob pattern and returns another files object only containing
// matched  files.
//
// This is designed to be called from a template.
//
// {{ range $name, $content := .Files.Glob("foo/**") }}
// {{ $name }}: |
// {{ .Files.Get($name) | indent 4 }}{{ end }}
func (f Files) Glob(pattern string) Files {
	g, err := glob.Compile(pattern, '/')
	if err != nil {
		g, _ = glob.Compile("**")
	}

	nf := NewFiles(nil)
	for name, contents := range f {
		if g.Match(name) {
			nf[name] = contents
		}
	}

	return nf
}

// AsConfig turns a Files group and flattens it to a YAML map suitable for
// including in the 'data' section of a Kubernetes ConfigMap definition.
// Duplicate keys will be overwritten, so be aware that your file names
// (regardless of path) should be unique.
//
// This is designed to be called from a template, and will return empty string
// (via ToYaml function) if it cannot be serialized to YAML, or if the Files
// object is nil.
//
// The output will not be indented, so you will want to pipe this to the
// 'indent' template function.
//
//   data:
// {{ .Files.Glob("config/**").AsConfig() | indent 4 }}
func (f Files) AsConfig() string {
	if f == nil {
		return ""
	}

	m := map[string]string{}

	// Explicitly convert to strings, and file names
	for k, v := range f {
		m[path.Base(k)] = string(v)
	}

	return ToYaml(m)
}

// AsSecrets returns the base64-encoded value of a Files object suitable for
// including in the 'data' section of a Kubernetes Secret definition.
// Duplicate keys will be overwritten, so be aware that your file names
// (regardless of path) should be unique.
//
// This is designed to be called from a template, and will return empty string
// (via ToYaml function) if it cannot be serialized to YAML, or if the Files
// object is nil.
//
// The output will not be indented, so you will want to pipe this to the
// 'indent' template function.
//
//   data:
// {{ .Files.Glob("secrets/*").AsSecrets() }}
func (f Files) AsSecrets() string {
	if f == nil {
		return ""
	}

	m := map[string]string{}

	for k, v := range f {
		m[path.Base(k)] = base64.StdEncoding.EncodeToString(v)
	}

	return ToYaml(m)
}

// Lines returns each line of a named file (split by "\n") as a slice, so it can
// be ranged over in your templates.
//
// This is designed to be called from a template.
//
// {{ range .Files.Lines "foo/bar.html" }}
// {{ . }}{{ end }}
func (f Files) Lines(path string) []string {
	if f == nil || f[path] == nil {
		return []string{}
	}

	return strings.Split(string(f[path]), "\n")
}

// ToYaml takes an interface, marshals it to yaml, and returns a string. It will
// always return a string, even on marshal error (empty string).
//
// This is designed to be called from a template.
func ToYaml(v interface{}) string {
	data, err := yaml.Marshal(v)
	if err != nil {
		// Swallow errors inside of a template.
		return ""
	}
	return string(data)
}

// FromYaml converts a YAML document into a map[string]interface{}.
//
// This is not a general-purpose YAML parser, and will not parse all valid
// YAML documents. Additionally, because its intended use is within templates
// it tolerates errors. It will insert the returned error message string into
// m["Error"] in the returned map.
func FromYaml(str string) map[string]interface{} {
	m := map[string]interface{}{}

	if err := yaml.Unmarshal([]byte(str), &m); err != nil {
		m["Error"] = err.Error()
	}
	return m
}

// ToToml takes an interface, marshals it to toml, and returns a string. It will
// always return a string, even on marshal error (empty string).
//
// This is designed to be called from a template.
func ToToml(v interface{}) string {
	b := bytes.NewBuffer(nil)
	e := toml.NewEncoder(b)
	err := e.Encode(v)
	if err != nil {
		return err.Error()
	}
	return b.String()
}

// ToJson takes an interface, marshals it to json, and returns a string. It will
// always return a string, even on marshal error (empty string).
//
// This is designed to be called from a template.
func ToJson(v interface{}) string {
	data, err := json.Marshal(v)
	if err != nil {
		// Swallow errors inside of a template.
		return ""
	}
	return string(data)
}

// FromJson converts a YAML document into a map[string]interface{}.
//
// This is not a general-purpose JSON parser, and will not parse all valid
// YAML documents. Additionally, because its intended use is within templates
// it tolerates errors. It will insert the returned error message string into
// m["Error"] in the returned map.
func FromJson(str string) map[string]interface{} {
	m := map[string]interface{}{}

	if err := json.Unmarshal([]byte(str), &m); err != nil {
		m["Error"] = err.Error()
	}
	return m
}
<commit_msg>docs: Fix FromJson comment<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.
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 chartutil

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"path"
	"strings"

	"github.com/ghodss/yaml"

	"github.com/BurntSushi/toml"
	"github.com/gobwas/glob"
	"github.com/golang/protobuf/ptypes/any"
)

// Files is a map of files in a chart that can be accessed from a template.
type Files map[string][]byte

// NewFiles creates a new Files from chart files.
// Given an []*any.Any (the format for files in a chart.Chart), extract a map of files.
func NewFiles(from []*any.Any) Files {
	files := map[string][]byte{}
	if from != nil {
		for _, f := range from {
			files[f.TypeUrl] = f.Value
		}
	}
	return files
}

// GetBytes gets a file by path.
//
// The returned data is raw. In a template context, this is identical to calling
// {{index .Files $path}}.
//
// This is intended to be accessed from within a template, so a missed key returns
// an empty []byte.
func (f Files) GetBytes(name string) []byte {
	v, ok := f[name]
	if !ok {
		return []byte{}
	}
	return v
}

// Get returns a string representation of the given file.
//
// Fetch the contents of a file as a string. It is designed to be called in a
// template.
//
//	{{.Files.Get "foo"}}
func (f Files) Get(name string) string {
	return string(f.GetBytes(name))
}

// Glob takes a glob pattern and returns another files object only containing
// matched  files.
//
// This is designed to be called from a template.
//
// {{ range $name, $content := .Files.Glob("foo/**") }}
// {{ $name }}: |
// {{ .Files.Get($name) | indent 4 }}{{ end }}
func (f Files) Glob(pattern string) Files {
	g, err := glob.Compile(pattern, '/')
	if err != nil {
		g, _ = glob.Compile("**")
	}

	nf := NewFiles(nil)
	for name, contents := range f {
		if g.Match(name) {
			nf[name] = contents
		}
	}

	return nf
}

// AsConfig turns a Files group and flattens it to a YAML map suitable for
// including in the 'data' section of a Kubernetes ConfigMap definition.
// Duplicate keys will be overwritten, so be aware that your file names
// (regardless of path) should be unique.
//
// This is designed to be called from a template, and will return empty string
// (via ToYaml function) if it cannot be serialized to YAML, or if the Files
// object is nil.
//
// The output will not be indented, so you will want to pipe this to the
// 'indent' template function.
//
//   data:
// {{ .Files.Glob("config/**").AsConfig() | indent 4 }}
func (f Files) AsConfig() string {
	if f == nil {
		return ""
	}

	m := map[string]string{}

	// Explicitly convert to strings, and file names
	for k, v := range f {
		m[path.Base(k)] = string(v)
	}

	return ToYaml(m)
}

// AsSecrets returns the base64-encoded value of a Files object suitable for
// including in the 'data' section of a Kubernetes Secret definition.
// Duplicate keys will be overwritten, so be aware that your file names
// (regardless of path) should be unique.
//
// This is designed to be called from a template, and will return empty string
// (via ToYaml function) if it cannot be serialized to YAML, or if the Files
// object is nil.
//
// The output will not be indented, so you will want to pipe this to the
// 'indent' template function.
//
//   data:
// {{ .Files.Glob("secrets/*").AsSecrets() }}
func (f Files) AsSecrets() string {
	if f == nil {
		return ""
	}

	m := map[string]string{}

	for k, v := range f {
		m[path.Base(k)] = base64.StdEncoding.EncodeToString(v)
	}

	return ToYaml(m)
}

// Lines returns each line of a named file (split by "\n") as a slice, so it can
// be ranged over in your templates.
//
// This is designed to be called from a template.
//
// {{ range .Files.Lines "foo/bar.html" }}
// {{ . }}{{ end }}
func (f Files) Lines(path string) []string {
	if f == nil || f[path] == nil {
		return []string{}
	}

	return strings.Split(string(f[path]), "\n")
}

// ToYaml takes an interface, marshals it to yaml, and returns a string. It will
// always return a string, even on marshal error (empty string).
//
// This is designed to be called from a template.
func ToYaml(v interface{}) string {
	data, err := yaml.Marshal(v)
	if err != nil {
		// Swallow errors inside of a template.
		return ""
	}
	return string(data)
}

// FromYaml converts a YAML document into a map[string]interface{}.
//
// This is not a general-purpose YAML parser, and will not parse all valid
// YAML documents. Additionally, because its intended use is within templates
// it tolerates errors. It will insert the returned error message string into
// m["Error"] in the returned map.
func FromYaml(str string) map[string]interface{} {
	m := map[string]interface{}{}

	if err := yaml.Unmarshal([]byte(str), &m); err != nil {
		m["Error"] = err.Error()
	}
	return m
}

// ToToml takes an interface, marshals it to toml, and returns a string. It will
// always return a string, even on marshal error (empty string).
//
// This is designed to be called from a template.
func ToToml(v interface{}) string {
	b := bytes.NewBuffer(nil)
	e := toml.NewEncoder(b)
	err := e.Encode(v)
	if err != nil {
		return err.Error()
	}
	return b.String()
}

// ToJson takes an interface, marshals it to json, and returns a string. It will
// always return a string, even on marshal error (empty string).
//
// This is designed to be called from a template.
func ToJson(v interface{}) string {
	data, err := json.Marshal(v)
	if err != nil {
		// Swallow errors inside of a template.
		return ""
	}
	return string(data)
}

// FromJson converts a JSON document into a map[string]interface{}.
//
// This is not a general-purpose JSON parser, and will not parse all valid
// JSON documents. Additionally, because its intended use is within templates
// it tolerates errors. It will insert the returned error message string into
// m["Error"] in the returned map.
func FromJson(str string) map[string]interface{} {
	m := map[string]interface{}{}

	if err := json.Unmarshal([]byte(str), &m); err != nil {
		m["Error"] = err.Error()
	}
	return m
}
<|endoftext|>