<commit_before>// Copyright 2016-2017 Authors of Cilium
//
// 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 labels

import (
	"bytes"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"sort"
	"strings"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/common"

	"github.com/op/go-logging"
)

var (
	log = logging.MustGetLogger("cilium-labels")
)

const (
	ID_NAME_ALL   = "all"
	ID_NAME_HOST  = "host"
	ID_NAME_WORLD = "world"
)

type LabelOpType string

const (
	AddLabelsOp     LabelOpType = "AddLabelsOp"
	DelLabelsOp     LabelOpType = "DelLabelsOp"
	EnableLabelsOp  LabelOpType = "EnableLabelsOp"
	DisableLabelsOp LabelOpType = "DisableLabelsOp"
)

type LabelOp map[LabelOpType]Labels

type OpLabels struct {
	// Active labels that are enabled and disabled but not deleted
	Custom Labels
	// Labels derived from orchestration system
	Orchestration Labels
	// Orchestration labels which have been disabled
	Disabled Labels
}

func (o *OpLabels) DeepCopy() *OpLabels {
	return &OpLabels{
		Custom:        o.Custom.DeepCopy(),
		Disabled:      o.Disabled.DeepCopy(),
		Orchestration: o.Orchestration.DeepCopy(),
	}
}

func (o *OpLabels) Enabled() Labels {
	enabled := make(Labels, len(o.Custom)+len(o.Orchestration))

	for k, v := range o.Custom {
		enabled[k] = v
	}

	for k, v := range o.Orchestration {
		enabled[k] = v
	}

	return enabled
}

func NewOplabelsFromModel(base *models.LabelConfiguration) *OpLabels {
	if base == nil {
		return nil
	}

	return &OpLabels{
		Custom:        NewLabelsFromModel(base.Custom),
		Disabled:      NewLabelsFromModel(base.Disabled),
		Orchestration: NewLabelsFromModel(base.OrchestrationSystem),
	}
}

type LabelOwner interface {
	ResolveName(name string) string
}

// Label is the cilium's representation of a container label.
type Label struct {
	Key   string `json:"key"`
	Value string `json:"value,omitempty"`
	// Source can be on of the values present in const.go (e.g.: CiliumLabelSource)
	Source string `json:"source"`
	absKey string
	// Mark element to be used to find unused labels in lists
	DeletionMark bool `json:"-"`
	owner        LabelOwner
}

// Labels is a map of labels where the map's key is the same as the label's key.
type Labels map[string]*Label

// MarkAllForDeletion marks all the labels with the DeletionMark.
func (l Labels) MarkAllForDeletion() {
	for k := range l {
		l[k].DeletionMark = true
	}
}

// DeleteMarked deletes the labels which have the DeletionMark set and returns
// true if any of them were deleted.
func (l Labels) DeleteMarked() bool {
	deleted := false
	for k := range l {
		if l[k].DeletionMark {
			delete(l, k)
			deleted = true
		}
	}

	return deleted
}

// AppendPrefixInKey appends the given prefix to all the Key's of the map and the
// respective Labels' Key.
func (l Labels) AppendPrefixInKey(prefix string) Labels {
	newLabels := Labels{}
	for k, v := range l {
		newLabels[prefix+k] = &Label{
			Key:    prefix + v.Key,
			Value:  v.Value,
			Source: v.Source,
			absKey: v.absKey,
		}
	}
	return newLabels
}

// NewLabel returns a new label from the given key, value and source. If source is empty,
// the default value will be common.CiliumLabelSource. If key starts with '$', the source
// will be overwritten with common.ReservedLabelSource. If key contains ':', the value
// before ':' will be used as source if given source is empty, otherwise the value before
// ':' will be deleted and unused.
func NewLabel(key string, value string, source string) *Label {
	var src string
	src, key = parseSource(key)
	if source == "" {
		if src == "" {
			source = common.CiliumLabelSource
		} else {
			source = src
		}
	}
	if src == common.ReservedLabelSource && key == "" {
		key = value
		value = ""
	}

	return &Label{
		Key:    key,
		Value:  value,
		Source: source,
	}
}

// NewOwnedLabel returns a new label like NewLabel but also assigns an owner
func NewOwnedLabel(key string, value string, source string, owner LabelOwner) *Label {
	l := NewLabel(key, value, source)
	l.SetOwner(owner)
	return l
}

// SetOwner modifies the owner of a label
func (l *Label) SetOwner(owner LabelOwner) {
	l.owner = owner
}

// Equals returns true if source, AbsoluteKey() and Value are equal and false otherwise.
func (l *Label) Equals(b *Label) bool {
	return l.Source == b.Source &&
		l.AbsoluteKey() == b.AbsoluteKey() &&
		l.Value == b.Value
}

func (l *Label) IsAllLabel() bool {
	// ID_NAME_ALL is a special label which matches all labels
	return l.Source == common.ReservedLabelSource && l.Key == "all"
}

func (l *Label) Matches(target *Label) bool {
	return l.IsAllLabel() || l.Equals(target)
}

// Resolve resolves the absolute key path for this Label from policyNode.
func (l *Label) Resolve(owner LabelOwner) {
	l.SetOwner(owner)

	// Force generation of absolute key
	l.AbsoluteKey()

	log.Debugf("Resolved label %s to path %s\n", l.String(), l.absKey)
}

// AbsoluteKey if set returns the absolute key path, otherwise returns the label's Key.
func (l *Label) AbsoluteKey() string {
	if l.absKey == "" {
		// Never translate using an owner if a reserved label
		if l.owner != nil && l.Source != common.ReservedLabelSource &&
			!strings.HasPrefix(l.Key, common.K8sPodNamespaceLabel) {
			l.absKey = l.owner.ResolveName(l.Key)
		} else {
			if !strings.HasPrefix(l.Key, "root.") {
				l.absKey = "root." + l.Key
			} else {
				l.absKey = l.Key
			}
		}
	}

	return l.absKey
}

// String returns the string representation of Label in the for of Source:Key=Value or
// Source:Key if Value is empty.
func (l Label) String() string {
	if len(l.Value) != 0 {
		return fmt.Sprintf("%s:%s=%s", l.Source, l.Key, l.Value)
	}
	return fmt.Sprintf("%s:%s", l.Source, l.Key)
}

// IsValid returns true if Key != "".
func (l *Label) IsValid() bool {
	return l.Key != ""
}

// UnmarshalJSON TODO create better explanation about unmarshall with examples
func (l *Label) UnmarshalJSON(data []byte) error {
	decoder := json.NewDecoder(bytes.NewReader(data))

	if l == nil {
		return fmt.Errorf("cannot unmarhshal to nil pointer")
	}

	if len(data) == 0 {
		return fmt.Errorf("invalid Label: empty data")
	}

	var aux struct {
		Source string `json:"source"`
		Key    string `json:"key"`
		Value  string `json:"value,omitempty"`
	}

	err := decoder.Decode(&aux)
	if err != nil {
		// If parsing of the full representation failed then try the short
		// form in the format:
		//
		// [SOURCE:]KEY[=VALUE]
		var aux string

		decoder = json.NewDecoder(bytes.NewReader(data))
		if err := decoder.Decode(&aux); err != nil {
			return fmt.Errorf("decode of Label as string failed: %+v", err)
		}

		if aux == "" {
			return fmt.Errorf("invalid Label: Failed to parse %s as a string", data)
		}

		*l = *ParseLabel(aux)
	} else {
		if aux.Key == "" {
			return fmt.Errorf("invalid Label: '%s' does not contain label key", data)
		}

		l.Source = aux.Source
		l.Key = aux.Key
		l.Value = aux.Value
	}

	return nil
}

// Map2Labels transforms in the form: map[key(string)]value(string) into Labels. The
// source argument will overwrite the source written in the key of the given map.
// Example:
// l := Map2Labels(map[string]string{"k8s:foo": "bar"}, "cilium")
// fmt.Printf("%+v\n", l)
//   map[string]Label{"foo":Label{Key:"foo", Value:"bar", Source:"cilium"}}
func Map2Labels(m map[string]string, source string) Labels {
	o := Labels{}
	for k, v := range m {
		l := NewLabel(k, v, source)
		o[l.Key] = l
	}
	return o
}

func (l Labels) DeepCopy() Labels {
	o := Labels{}
	for k, v := range l {
		o[k] = &Label{
			Key:    v.Key,
			Value:  v.Value,
			Source: v.Source,
			absKey: v.absKey,
		}
	}
	return o
}

func NewLabelsFromModel(base []string) Labels {
	lbls := Labels{}
	for _, v := range base {
		lbl := ParseLabel(v)
		lbls[lbl.Key] = lbl
	}

	return lbls
}

func (l Labels) GetModel() []string {
	res := []string{}
	for _, v := range l {
		res = append(res, v.String())
	}
	return res
}

// MergeLabels merges labels from into to. It overwrites all labels with the same Key as
// from written into to.
// Example:
// to := Labels{Label{key1, value1, source1}, Label{key2, value3, source4}}
// from := Labels{Label{key1, value3, source4}}
// to.MergeLabels(from)
// fmt.Printf("%+v\n", to)
//   Labels{Label{key1, value3, source4}, Label{key2, value3, source4}}
func (l Labels) MergeLabels(from Labels) {
	fromCpy := from.DeepCopy()
	for k, v := range fromCpy {
		l[k] = v
	}
}

// SHA256Sum calculates l' internal SHA256Sum. For a particular set of labels is
// guarantee that it will always have the same SHA256Sum.
func (l Labels) SHA256Sum() string {
	return fmt.Sprintf("%x", sha512.New512_256().Sum(l.sortedList()))
}

func (l Labels) sortedList() []byte {
	var keys []string
	for k := range l {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	result := ""
	for _, k := range keys {
		// We don't care if the values already have a '=' since this method is
		// only used to calculate a SHA256Sum
		result += fmt.Sprintf(`%s=%s;`, k, l[k].Value)
	}

	return []byte(result)
}

// ToSlice returns a slice of label with the values of the given Labels' map.
func (l Labels) ToSlice() []Label {
	labels := []Label{}
	for _, v := range l {
		labels = append(labels, *v)
	}
	return labels
}

// LabelSlice2LabelsMap returns a Labels' map with all labels from the given slice of
// label.
func LabelSlice2LabelsMap(lbls []Label) Labels {
	labels := Labels{}
	for _, v := range lbls {
		labels[v.Key] = NewLabel(v.Key, v.Value, v.Source)
	}
	return labels
}

// parseSource returns the parsed source of the given str. It also returns the next piece
// of text that is after the source.
// Example:
//  src, next := parseSource("foo:bar==value")
// Println(src) // foo
// Println(next) // bar==value
func parseSource(str string) (src, next string) {
	if str == "" {
		return "", ""
	}
	if str[0] == '$' {
		str = strings.Replace(str, "$", common.ReservedLabelSource+":", 1)
	}
	sourceSplit := strings.SplitN(str, ":", 2)
	if len(sourceSplit) != 2 {
		next = sourceSplit[0]
		if strings.HasPrefix(next, common.ReservedLabelKey) {
			src = common.ReservedLabelSource
			next = strings.TrimPrefix(next, common.ReservedLabelKey)
		}
	} else {
		if sourceSplit[0] != "" {
			src = sourceSplit[0]
		}
		next = sourceSplit[1]
	}
	return
}

// ParseLabel returns the label representation of the given string. The str should be
// in the form of Source:Key=Value or Source:Key if Value is empty. It also parses short
// forms, for example: $host will be Label{Key: "host", Source: "reserved", Value: ""}.
func ParseLabel(str string) *Label {
	lbl := Label{}
	src, next := parseSource(str)
	if src != "" {
		lbl.Source = src
	} else {
		lbl.Source = common.CiliumLabelSource
	}

	keySplit := strings.SplitN(next, "=", 2)
	lbl.Key = keySplit[0]
	if len(keySplit) > 1 {
		if src == common.ReservedLabelSource && keySplit[0] == "" {
			lbl.Key = keySplit[1]
		} else {
			lbl.Value = keySplit[1]
		}
	}
	return &lbl
}

func ParseStringLabels(strLbls []string) Labels {
	lbls := Labels{}
	for _, l := range strLbls {
		lbl := ParseLabel(l)
		lbls[lbl.Key] = lbl
	}

	return lbls
}

func LabelSliceSHA256Sum(labels []Label) (string, error) {
	sha := sha512.New512_256()
	if err := json.NewEncoder(sha).Encode(labels); err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", sha.Sum(nil)), nil
}

func ParseStringLabelsInOrder(strLbls []string) []Label {
	lbls := []Label{}
	for _, l := range strLbls {
		lbl := ParseLabel(l)
		lbls = append(lbls, *lbl)
	}

	return lbls
}
<commit_msg>labels: Removed unused const values<commit_after>// Copyright 2016-2017 Authors of Cilium
//
// 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 labels

import (
	"bytes"
	"crypto/sha512"
	"encoding/json"
	"fmt"
	"sort"
	"strings"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/common"

	"github.com/op/go-logging"
)

var (
	log = logging.MustGetLogger("cilium-labels")
)

const (
	ID_NAME_ALL   = "all"
	ID_NAME_HOST  = "host"
	ID_NAME_WORLD = "world"
)

type OpLabels struct {
	// Active labels that are enabled and disabled but not deleted
	Custom Labels
	// Labels derived from orchestration system
	Orchestration Labels
	// Orchestration labels which have been disabled
	Disabled Labels
}

func (o *OpLabels) DeepCopy() *OpLabels {
	return &OpLabels{
		Custom:        o.Custom.DeepCopy(),
		Disabled:      o.Disabled.DeepCopy(),
		Orchestration: o.Orchestration.DeepCopy(),
	}
}

func (o *OpLabels) Enabled() Labels {
	enabled := make(Labels, len(o.Custom)+len(o.Orchestration))

	for k, v := range o.Custom {
		enabled[k] = v
	}

	for k, v := range o.Orchestration {
		enabled[k] = v
	}

	return enabled
}

func NewOplabelsFromModel(base *models.LabelConfiguration) *OpLabels {
	if base == nil {
		return nil
	}

	return &OpLabels{
		Custom:        NewLabelsFromModel(base.Custom),
		Disabled:      NewLabelsFromModel(base.Disabled),
		Orchestration: NewLabelsFromModel(base.OrchestrationSystem),
	}
}

type LabelOwner interface {
	ResolveName(name string) string
}

// Label is the cilium's representation of a container label.
type Label struct {
	Key   string `json:"key"`
	Value string `json:"value,omitempty"`
	// Source can be on of the values present in const.go (e.g.: CiliumLabelSource)
	Source string `json:"source"`
	absKey string
	// Mark element to be used to find unused labels in lists
	DeletionMark bool `json:"-"`
	owner        LabelOwner
}

// Labels is a map of labels where the map's key is the same as the label's key.
type Labels map[string]*Label

// MarkAllForDeletion marks all the labels with the DeletionMark.
func (l Labels) MarkAllForDeletion() {
	for k := range l {
		l[k].DeletionMark = true
	}
}

// DeleteMarked deletes the labels which have the DeletionMark set and returns
// true if any of them were deleted.
func (l Labels) DeleteMarked() bool {
	deleted := false
	for k := range l {
		if l[k].DeletionMark {
			delete(l, k)
			deleted = true
		}
	}

	return deleted
}

// AppendPrefixInKey appends the given prefix to all the Key's of the map and the
// respective Labels' Key.
func (l Labels) AppendPrefixInKey(prefix string) Labels {
	newLabels := Labels{}
	for k, v := range l {
		newLabels[prefix+k] = &Label{
			Key:    prefix + v.Key,
			Value:  v.Value,
			Source: v.Source,
			absKey: v.absKey,
		}
	}
	return newLabels
}

// NewLabel returns a new label from the given key, value and source. If source is empty,
// the default value will be common.CiliumLabelSource. If key starts with '$', the source
// will be overwritten with common.ReservedLabelSource. If key contains ':', the value
// before ':' will be used as source if given source is empty, otherwise the value before
// ':' will be deleted and unused.
func NewLabel(key string, value string, source string) *Label {
	var src string
	src, key = parseSource(key)
	if source == "" {
		if src == "" {
			source = common.CiliumLabelSource
		} else {
			source = src
		}
	}
	if src == common.ReservedLabelSource && key == "" {
		key = value
		value = ""
	}

	return &Label{
		Key:    key,
		Value:  value,
		Source: source,
	}
}

// NewOwnedLabel returns a new label like NewLabel but also assigns an owner
func NewOwnedLabel(key string, value string, source string, owner LabelOwner) *Label {
	l := NewLabel(key, value, source)
	l.SetOwner(owner)
	return l
}

// SetOwner modifies the owner of a label
func (l *Label) SetOwner(owner LabelOwner) {
	l.owner = owner
}

// Equals returns true if source, AbsoluteKey() and Value are equal and false otherwise.
func (l *Label) Equals(b *Label) bool {
	return l.Source == b.Source &&
		l.AbsoluteKey() == b.AbsoluteKey() &&
		l.Value == b.Value
}

func (l *Label) IsAllLabel() bool {
	// ID_NAME_ALL is a special label which matches all labels
	return l.Source == common.ReservedLabelSource && l.Key == "all"
}

func (l *Label) Matches(target *Label) bool {
	return l.IsAllLabel() || l.Equals(target)
}

// Resolve resolves the absolute key path for this Label from policyNode.
func (l *Label) Resolve(owner LabelOwner) {
	l.SetOwner(owner)

	// Force generation of absolute key
	l.AbsoluteKey()

	log.Debugf("Resolved label %s to path %s\n", l.String(), l.absKey)
}

// AbsoluteKey if set returns the absolute key path, otherwise returns the label's Key.
func (l *Label) AbsoluteKey() string {
	if l.absKey == "" {
		// Never translate using an owner if a reserved label
		if l.owner != nil && l.Source != common.ReservedLabelSource &&
			!strings.HasPrefix(l.Key, common.K8sPodNamespaceLabel) {
			l.absKey = l.owner.ResolveName(l.Key)
		} else {
			if !strings.HasPrefix(l.Key, "root.") {
				l.absKey = "root." + l.Key
			} else {
				l.absKey = l.Key
			}
		}
	}

	return l.absKey
}

// String returns the string representation of Label in the for of Source:Key=Value or
// Source:Key if Value is empty.
func (l Label) String() string {
	if len(l.Value) != 0 {
		return fmt.Sprintf("%s:%s=%s", l.Source, l.Key, l.Value)
	}
	return fmt.Sprintf("%s:%s", l.Source, l.Key)
}

// IsValid returns true if Key != "".
func (l *Label) IsValid() bool {
	return l.Key != ""
}

// UnmarshalJSON TODO create better explanation about unmarshall with examples
func (l *Label) UnmarshalJSON(data []byte) error {
	decoder := json.NewDecoder(bytes.NewReader(data))

	if l == nil {
		return fmt.Errorf("cannot unmarhshal to nil pointer")
	}

	if len(data) == 0 {
		return fmt.Errorf("invalid Label: empty data")
	}

	var aux struct {
		Source string `json:"source"`
		Key    string `json:"key"`
		Value  string `json:"value,omitempty"`
	}

	err := decoder.Decode(&aux)
	if err != nil {
		// If parsing of the full representation failed then try the short
		// form in the format:
		//
		// [SOURCE:]KEY[=VALUE]
		var aux string

		decoder = json.NewDecoder(bytes.NewReader(data))
		if err := decoder.Decode(&aux); err != nil {
			return fmt.Errorf("decode of Label as string failed: %+v", err)
		}

		if aux == "" {
			return fmt.Errorf("invalid Label: Failed to parse %s as a string", data)
		}

		*l = *ParseLabel(aux)
	} else {
		if aux.Key == "" {
			return fmt.Errorf("invalid Label: '%s' does not contain label key", data)
		}

		l.Source = aux.Source
		l.Key = aux.Key
		l.Value = aux.Value
	}

	return nil
}

// Map2Labels transforms in the form: map[key(string)]value(string) into Labels. The
// source argument will overwrite the source written in the key of the given map.
// Example:
// l := Map2Labels(map[string]string{"k8s:foo": "bar"}, "cilium")
// fmt.Printf("%+v\n", l)
//   map[string]Label{"foo":Label{Key:"foo", Value:"bar", Source:"cilium"}}
func Map2Labels(m map[string]string, source string) Labels {
	o := Labels{}
	for k, v := range m {
		l := NewLabel(k, v, source)
		o[l.Key] = l
	}
	return o
}

func (l Labels) DeepCopy() Labels {
	o := Labels{}
	for k, v := range l {
		o[k] = &Label{
			Key:    v.Key,
			Value:  v.Value,
			Source: v.Source,
			absKey: v.absKey,
		}
	}
	return o
}

func NewLabelsFromModel(base []string) Labels {
	lbls := Labels{}
	for _, v := range base {
		lbl := ParseLabel(v)
		lbls[lbl.Key] = lbl
	}

	return lbls
}

func (l Labels) GetModel() []string {
	res := []string{}
	for _, v := range l {
		res = append(res, v.String())
	}
	return res
}

// MergeLabels merges labels from into to. It overwrites all labels with the same Key as
// from written into to.
// Example:
// to := Labels{Label{key1, value1, source1}, Label{key2, value3, source4}}
// from := Labels{Label{key1, value3, source4}}
// to.MergeLabels(from)
// fmt.Printf("%+v\n", to)
//   Labels{Label{key1, value3, source4}, Label{key2, value3, source4}}
func (l Labels) MergeLabels(from Labels) {
	fromCpy := from.DeepCopy()
	for k, v := range fromCpy {
		l[k] = v
	}
}

// SHA256Sum calculates l' internal SHA256Sum. For a particular set of labels is
// guarantee that it will always have the same SHA256Sum.
func (l Labels) SHA256Sum() string {
	return fmt.Sprintf("%x", sha512.New512_256().Sum(l.sortedList()))
}

func (l Labels) sortedList() []byte {
	var keys []string
	for k := range l {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	result := ""
	for _, k := range keys {
		// We don't care if the values already have a '=' since this method is
		// only used to calculate a SHA256Sum
		result += fmt.Sprintf(`%s=%s;`, k, l[k].Value)
	}

	return []byte(result)
}

// ToSlice returns a slice of label with the values of the given Labels' map.
func (l Labels) ToSlice() []Label {
	labels := []Label{}
	for _, v := range l {
		labels = append(labels, *v)
	}
	return labels
}

// LabelSlice2LabelsMap returns a Labels' map with all labels from the given slice of
// label.
func LabelSlice2LabelsMap(lbls []Label) Labels {
	labels := Labels{}
	for _, v := range lbls {
		labels[v.Key] = NewLabel(v.Key, v.Value, v.Source)
	}
	return labels
}

// parseSource returns the parsed source of the given str. It also returns the next piece
// of text that is after the source.
// Example:
//  src, next := parseSource("foo:bar==value")
// Println(src) // foo
// Println(next) // bar==value
func parseSource(str string) (src, next string) {
	if str == "" {
		return "", ""
	}
	if str[0] == '$' {
		str = strings.Replace(str, "$", common.ReservedLabelSource+":", 1)
	}
	sourceSplit := strings.SplitN(str, ":", 2)
	if len(sourceSplit) != 2 {
		next = sourceSplit[0]
		if strings.HasPrefix(next, common.ReservedLabelKey) {
			src = common.ReservedLabelSource
			next = strings.TrimPrefix(next, common.ReservedLabelKey)
		}
	} else {
		if sourceSplit[0] != "" {
			src = sourceSplit[0]
		}
		next = sourceSplit[1]
	}
	return
}

// ParseLabel returns the label representation of the given string. The str should be
// in the form of Source:Key=Value or Source:Key if Value is empty. It also parses short
// forms, for example: $host will be Label{Key: "host", Source: "reserved", Value: ""}.
func ParseLabel(str string) *Label {
	lbl := Label{}
	src, next := parseSource(str)
	if src != "" {
		lbl.Source = src
	} else {
		lbl.Source = common.CiliumLabelSource
	}

	keySplit := strings.SplitN(next, "=", 2)
	lbl.Key = keySplit[0]
	if len(keySplit) > 1 {
		if src == common.ReservedLabelSource && keySplit[0] == "" {
			lbl.Key = keySplit[1]
		} else {
			lbl.Value = keySplit[1]
		}
	}
	return &lbl
}

func ParseStringLabels(strLbls []string) Labels {
	lbls := Labels{}
	for _, l := range strLbls {
		lbl := ParseLabel(l)
		lbls[lbl.Key] = lbl
	}

	return lbls
}

func LabelSliceSHA256Sum(labels []Label) (string, error) {
	sha := sha512.New512_256()
	if err := json.NewEncoder(sha).Encode(labels); err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", sha.Sum(nil)), nil
}

func ParseStringLabelsInOrder(strLbls []string) []Label {
	lbls := []Label{}
	for _, l := range strLbls {
		lbl := ParseLabel(l)
		lbls = append(lbls, *lbl)
	}

	return lbls
}
<|endoftext|>


<commit_before>// Command mdrun can be used to test the md package. Run it with "go run".
package main

import (
	"flag"
	"fmt"
	"io"
	"os"

	"src.elv.sh/pkg/md"
)

var (
	codec = flag.String("codec", "html", "codec to use; one of html, trace, fmt, tty")
	width = flag.Int("width", 0, "text width; relevant with fmt or tty")
)

func main() {
	flag.Parse()

	c := getCodec(*codec)
	bs, err := io.ReadAll(os.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, "read stdin:", err)
		os.Exit(2)
	}
	fmt.Print(md.RenderString(string(bs), c))
}

func getCodec(s string) md.StringerCodec {
	switch *codec {
	case "html":
		return &md.HTMLCodec{}
	case "trace":
		return &md.TraceCodec{}
	case "fmt":
		return &md.FmtCodec{Width: *width}
	case "tty":
		return &md.TTYCodec{Width: *width}
	default:
		fmt.Println("unknown codec:", s)
		os.Exit(2)
		return nil
	}
}
<commit_msg>pkg/md/mdrun: Support -cpuprofile.<commit_after>// Command mdrun can be used to test the md package. Run it with "go run".
package main

import (
	"flag"
	"fmt"
	"io"
	"os"
	"runtime/pprof"

	"src.elv.sh/pkg/md"
)

var (
	cpuprofile = flag.String("cpuprofile", "", "name of file to store CPU profile in")
	codec      = flag.String("codec", "html", "codec to use; one of html, trace, fmt, tty")
	width      = flag.Int("width", 0, "text width; relevant with fmt or tty")
)

func main() {
	flag.Parse()

	c := getCodec(*codec)
	bs, err := io.ReadAll(os.Stdin)
	if err != nil {
		fmt.Fprintln(os.Stderr, "read stdin:", err)
		os.Exit(2)
	}
	if *cpuprofile != "" {
		f, err := os.OpenFile(*cpuprofile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
		if err != nil {
			fmt.Printf("create cpu profile file %q: %v\n", *cpuprofile, err)
			os.Exit(2)
		}
		defer f.Close()
		err = pprof.StartCPUProfile(f)
		if err != nil {
			fmt.Println("start cpu profile:", err)
			os.Exit(2)
		}
		defer pprof.StopCPUProfile()
	}
	fmt.Print(md.RenderString(string(bs), c))
}

func getCodec(s string) md.StringerCodec {
	switch *codec {
	case "html":
		return &md.HTMLCodec{}
	case "trace":
		return &md.TraceCodec{}
	case "fmt":
		return &md.FmtCodec{Width: *width}
	case "tty":
		return &md.TTYCodec{Width: *width}
	default:
		fmt.Println("unknown codec:", s)
		os.Exit(2)
		return nil
	}
}
<|endoftext|>


<commit_before>package monitor

import (
	"context"
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/openshift/origin/pkg/monitor/monitorapi"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
)

func startEventMonitoring(ctx context.Context, m Recorder, client kubernetes.Interface) {
	reMatchFirstQuote := regexp.MustCompile(`"([^"]+)"( in (\d+(\.\d+)?(s|ms)$))?`)

	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			default:
			}

			// filter out events written "now" but with significantly older start times (events
			// created in test jobs are the most common)
			significantlyBeforeNow := time.Now().UTC().Add(-15 * time.Minute)

			events, err := client.CoreV1().Events("").List(ctx, metav1.ListOptions{Limit: 1})
			if err != nil {
				continue
			}
			rv := events.ResourceVersion

			for i := range events.Items {
				m.RecordResource("events", &events.Items[i])
			}

			for expired := false; !expired; {
				w, err := client.CoreV1().Events("").Watch(ctx, metav1.ListOptions{ResourceVersion: rv})
				if err != nil {
					if errors.IsResourceExpired(err) {
						break
					}
					continue
				}
				w = watch.Filter(w, func(in watch.Event) (watch.Event, bool) {
					// TODO: gathering all events results in a 4x increase in e2e.log size, but is is
					//       valuable enough to gather that the cost is worth it
					// return in, filterToSystemNamespaces(in.Object)
					return in, true
				})
				func() {
					defer w.Stop()
					for event := range w.ResultChan() {
						switch event.Type {
						case watch.Added, watch.Modified:
							obj, ok := event.Object.(*corev1.Event)
							if !ok {
								continue
							}
							m.RecordResource("events", obj)

							t := obj.LastTimestamp.Time
							if t.IsZero() {
								t = obj.EventTime.Time
							}
							if t.IsZero() {
								t = obj.CreationTimestamp.Time
							}
							if t.Before(significantlyBeforeNow) {
								break
							}

							message := obj.Message
							if obj.Count > 1 {
								message += fmt.Sprintf(" (%d times)", obj.Count)
							}

							if obj.InvolvedObject.Kind == "Node" {
								if node, err := client.CoreV1().Nodes().Get(ctx, obj.InvolvedObject.Name, metav1.GetOptions{}); err == nil {
									message = fmt.Sprintf("roles/%s %s", nodeRoles(node), message)
								}
							}

							// special case some very common events
							switch obj.Reason {
							case "":
							case "Scheduled":
								if obj.InvolvedObject.Kind == "Pod" {
									if strings.HasPrefix(message, "Successfully assigned ") {
										if i := strings.Index(message, " to "); i != -1 {
											node := message[i+4:]
											message = fmt.Sprintf("node/%s reason/%s", node, obj.Reason)
											break
										}
									}
								}
								message = fmt.Sprintf("reason/%s %s", obj.Reason, message)
							case "Started", "Created", "Killing":
								if obj.InvolvedObject.Kind == "Pod" {
									if containerName, ok := eventForContainer(obj.InvolvedObject.FieldPath); ok {
										message = fmt.Sprintf("container/%s reason/%s", containerName, obj.Reason)
										break
									}
								}
								message = fmt.Sprintf("reason/%s %s", obj.Reason, message)
							case "Pulling", "Pulled":
								if obj.InvolvedObject.Kind == "Pod" {
									if containerName, ok := eventForContainer(obj.InvolvedObject.FieldPath); ok {
										if m := reMatchFirstQuote.FindStringSubmatch(obj.Message); m != nil {
											if len(m) > 3 {
												if d, err := time.ParseDuration(m[3]); err == nil {
													message = fmt.Sprintf("container/%s reason/%s duration/%.3fs image/%s", containerName, obj.Reason, d.Seconds(), m[1])
													break
												}
											}
											message = fmt.Sprintf("container/%s reason/%s image/%s", containerName, obj.Reason, m[1])
											break
										}
									}
								}
								message = fmt.Sprintf("reason/%s %s", obj.Reason, message)
							default:
								message = fmt.Sprintf("reason/%s %s", obj.Reason, message)
							}
							condition := monitorapi.Condition{
								Level:   monitorapi.Info,
								Locator: locateEvent(obj),
								Message: message,
							}
							if obj.Type == corev1.EventTypeWarning {
								condition.Level = monitorapi.Warning
							}
							m.RecordAt(t, condition)
						case watch.Error:
							var message string
							if status, ok := event.Object.(*metav1.Status); ok {
								if err := errors.FromObject(status); err != nil && errors.IsResourceExpired(err) {
									expired = true
									return
								}
								message = status.Message
							} else {
								message = fmt.Sprintf("event object was not a Status: %T", event.Object)
							}
							m.Record(monitorapi.Condition{
								Level:   monitorapi.Info,
								Locator: "kube-apiserver",
								Message: fmt.Sprintf("received an error while watching events: %s", message),
							})
							return
						default:
						}
					}
				}()
			}
		}
	}()
}

func eventForContainer(fieldPath string) (string, bool) {
	if !strings.HasSuffix(fieldPath, "}") {
		return "", false
	}
	fieldPath = strings.TrimSuffix(fieldPath, "}")
	switch {
	case strings.HasPrefix(fieldPath, "spec.containers{"):
		return strings.TrimPrefix(fieldPath, "spec.containers{"), true
	case strings.HasPrefix(fieldPath, "spec.initContainers{"):
		return strings.TrimPrefix(fieldPath, "spec.initContainers{"), true
	default:
		return "", false
	}
}
<commit_msg>Debugging hack for missing event intervals.<commit_after>package monitor

import (
	"context"
	"fmt"
	"regexp"
	"strings"
	"time"

	"github.com/openshift/origin/pkg/monitor/monitorapi"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/kubernetes"
)

func startEventMonitoring(ctx context.Context, m Recorder, client kubernetes.Interface) {
	reMatchFirstQuote := regexp.MustCompile(`"([^"]+)"( in (\d+(\.\d+)?(s|ms)$))?`)

	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			default:
			}

			// filter out events written "now" but with significantly older start times (events
			// created in test jobs are the most common)
			significantlyBeforeNow := time.Now().UTC().Add(-15 * time.Minute)

			events, err := client.CoreV1().Events("").List(ctx, metav1.ListOptions{Limit: 1})
			if err != nil {
				continue
			}
			rv := events.ResourceVersion

			for i := range events.Items {
				m.RecordResource("events", &events.Items[i])
			}

			for expired := false; !expired; {
				w, err := client.CoreV1().Events("").Watch(ctx, metav1.ListOptions{ResourceVersion: rv})
				if err != nil {
					if errors.IsResourceExpired(err) {
						break
					}
					continue
				}
				w = watch.Filter(w, func(in watch.Event) (watch.Event, bool) {
					// TODO: gathering all events results in a 4x increase in e2e.log size, but is is
					//       valuable enough to gather that the cost is worth it
					// return in, filterToSystemNamespaces(in.Object)
					return in, true
				})
				func() {
					defer w.Stop()
					for event := range w.ResultChan() {
						switch event.Type {
						case watch.Added, watch.Modified:
							obj, ok := event.Object.(*corev1.Event)
							if !ok {
								continue
							}
							m.RecordResource("events", obj)

							// Temporary hack by dgoodwin, we're missing events here that show up later in
							// gather-extra/events.json. Adding some output to see if we can isolate what we saw
							// and where it might have been filtered out.
							osEvent := false
							if obj.Reason == "OSUpdateStaged" || obj.Reason == "OSUpdateStarted" {
								osEvent = true
								fmt.Printf("Watch received OS update event: %s - %s - %s\n",
									obj.Reason, obj.InvolvedObject.Name, obj.LastTimestamp.Format(time.RFC3339))

							}
							t := obj.LastTimestamp.Time
							if t.IsZero() {
								t = obj.EventTime.Time
							}
							if t.IsZero() {
								t = obj.CreationTimestamp.Time
							}
							if t.Before(significantlyBeforeNow) {
								if osEvent {
									fmt.Printf("OS update event filtered for being too old: %s - %s - %s (now: %s)\n",
										obj.Reason, obj.InvolvedObject.Name, obj.LastTimestamp.Format(time.RFC3339),
										time.Now().Format(time.RFC3339))
								}
								break
							}

							message := obj.Message
							if obj.Count > 1 {
								message += fmt.Sprintf(" (%d times)", obj.Count)
							}

							if obj.InvolvedObject.Kind == "Node" {
								if node, err := client.CoreV1().Nodes().Get(ctx, obj.InvolvedObject.Name, metav1.GetOptions{}); err == nil {
									message = fmt.Sprintf("roles/%s %s", nodeRoles(node), message)
								}
							}

							// special case some very common events
							switch obj.Reason {
							case "":
							case "Scheduled":
								if obj.InvolvedObject.Kind == "Pod" {
									if strings.HasPrefix(message, "Successfully assigned ") {
										if i := strings.Index(message, " to "); i != -1 {
											node := message[i+4:]
											message = fmt.Sprintf("node/%s reason/%s", node, obj.Reason)
											break
										}
									}
								}
								message = fmt.Sprintf("reason/%s %s", obj.Reason, message)
							case "Started", "Created", "Killing":
								if obj.InvolvedObject.Kind == "Pod" {
									if containerName, ok := eventForContainer(obj.InvolvedObject.FieldPath); ok {
										message = fmt.Sprintf("container/%s reason/%s", containerName, obj.Reason)
										break
									}
								}
								message = fmt.Sprintf("reason/%s %s", obj.Reason, message)
							case "Pulling", "Pulled":
								if obj.InvolvedObject.Kind == "Pod" {
									if containerName, ok := eventForContainer(obj.InvolvedObject.FieldPath); ok {
										if m := reMatchFirstQuote.FindStringSubmatch(obj.Message); m != nil {
											if len(m) > 3 {
												if d, err := time.ParseDuration(m[3]); err == nil {
													message = fmt.Sprintf("container/%s reason/%s duration/%.3fs image/%s", containerName, obj.Reason, d.Seconds(), m[1])
													break
												}
											}
											message = fmt.Sprintf("container/%s reason/%s image/%s", containerName, obj.Reason, m[1])
											break
										}
									}
								}
								message = fmt.Sprintf("reason/%s %s", obj.Reason, message)
							default:
								message = fmt.Sprintf("reason/%s %s", obj.Reason, message)
							}
							condition := monitorapi.Condition{
								Level:   monitorapi.Info,
								Locator: locateEvent(obj),
								Message: message,
							}
							if obj.Type == corev1.EventTypeWarning {
								condition.Level = monitorapi.Warning
							}
							m.RecordAt(t, condition)
						case watch.Error:
							var message string
							if status, ok := event.Object.(*metav1.Status); ok {
								if err := errors.FromObject(status); err != nil && errors.IsResourceExpired(err) {
									expired = true
									return
								}
								message = status.Message
							} else {
								message = fmt.Sprintf("event object was not a Status: %T", event.Object)
							}
							m.Record(monitorapi.Condition{
								Level:   monitorapi.Info,
								Locator: "kube-apiserver",
								Message: fmt.Sprintf("received an error while watching events: %s", message),
							})
							return
						default:
						}
					}
				}()
			}
		}
	}()
}

func eventForContainer(fieldPath string) (string, bool) {
	if !strings.HasSuffix(fieldPath, "}") {
		return "", false
	}
	fieldPath = strings.TrimSuffix(fieldPath, "}")
	switch {
	case strings.HasPrefix(fieldPath, "spec.containers{"):
		return strings.TrimPrefix(fieldPath, "spec.containers{"), true
	case strings.HasPrefix(fieldPath, "spec.initContainers{"):
		return strings.TrimPrefix(fieldPath, "spec.initContainers{"), true
	default:
		return "", false
	}
}
<|endoftext|>


<commit_before>/*******************************************************************************
*
* Copyright 2017 SAP SE
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You should have received a copy of the License along with this
* program. If not, 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 plugins

import (
	"net/http"
	"regexp"
	"strconv"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack"
	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts"
	"github.com/sapcc/limes/pkg/limes"
	"github.com/sapcc/limes/pkg/util"
)

type swiftPlugin struct{}

var swiftResources = []limes.ResourceInfo{
	limes.ResourceInfo{
		Name: "capacity",
		Unit: limes.UnitBytes,
	},
}

//TODO Make Auth prefix configurable
var urlRegex = regexp.MustCompile("(v1/AUTH_)[a-zA-Z0-9]+")

func init() {
	limes.RegisterQuotaPlugin(&swiftPlugin{})
}

//ServiceType implements the limes.Plugin interface.
func (p *swiftPlugin) ServiceType() string {
	return "object-store"
}

//Resources implements the limes.Plugin interface.
func (p *swiftPlugin) Resources() []limes.ResourceInfo {
	return swiftResources
}

func (p *swiftPlugin) Client(driver limes.Driver) (*gophercloud.ServiceClient, error) {
	return openstack.NewObjectStorageV1(driver.Client(),
		gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic},
	)
}

//Scrape implements the limes.Plugin interface.
func (p *swiftPlugin) Scrape(driver limes.Driver, domainUUID, projectUUID string) (map[string]limes.ResourceData, error) {
	client, err := p.projectClient(driver, projectUUID)
	if err != nil {
		return nil, err
	}

	//Get account metadata
	account := getAccount(client, projectUUID)
	if account == nil {
		//Swift account does not exist, but the keystone project
		return map[string]limes.ResourceData{
			"capacity": limes.ResourceData{
				Quota: 0,
				Usage: 0,
			},
		}, nil
	} else if account.Err != nil {
		return nil, err
	}

	//Extract quota, if set
	var quota int64 = -1
	quotaHeader := account.Header.Get("X-Account-Meta-Quota-Bytes")
	if quotaHeader != "" {
		quota, _ = strconv.ParseInt(quotaHeader, 10, 64)
	}

	//Extract usage
	var usage int64
	usageHeader := account.Header.Get("X-Account-Bytes-Used")
	if usageHeader != "" {
		usage, _ = strconv.ParseInt(usageHeader, 10, 64)
	}

	util.LogDebug("Swift Account %s: quota '%d' - usage '%d'", projectUUID, quota, usage)
	return map[string]limes.ResourceData{
		"capacity": limes.ResourceData{
			Quota: quota,
			Usage: uint64(usage),
		},
	}, nil
}

//SetQuota implements the limes.Plugin interface.
func (p *swiftPlugin) SetQuota(driver limes.Driver, domainUUID, projectUUID string, quotas map[string]uint64) error {
	client, err := p.projectClient(driver, projectUUID)
	if err != nil {
		return err
	}

	headers := make(map[string]string)
	headers["X-Account-Meta-Quota-Bytes"] = string(quotas["capacity"])
	//this header brought to you by https://github.com/sapcc/swift-addons
	headers["X-Account-Project-Domain-Id-Override"] = domainUUID

	result, err := updateAccount(client, headers)
	if result.StatusCode == http.StatusNotFound && quotas["capacity"] > 0 {
		//account does not exist yet - if there is a non-zero quota, enable it now
		_, err = putAccount(client, headers)
		if err != nil {
			util.LogInfo("Swift Account %s created", projectUUID)
		}
	}
	return err
}

//Get the project scoped cliet with specific storage URL
func (p *swiftPlugin) projectClient(driver limes.Driver, projectUUID string) (*gophercloud.ServiceClient, error) {
	client, err := p.Client(driver)
	if err != nil {
		return nil, err
	}

	//We act as Reseller_Admin here, but cannot use the endpoint url returned from catalogue
	//Replace the resellers project id with the requested one
	client.Endpoint = urlRegex.ReplaceAllString(client.Endpoint, "${1}"+projectUUID)
	util.LogDebug(client.Endpoint)
	return client, nil
}

//Wrapping the accounts.Get because the swift account might not be created if account_auto_create = false
func getAccount(client *gophercloud.ServiceClient, projectUUID string) *accounts.GetResult {
	//Get account metadata
	var result accounts.GetResult
	result = accounts.Get(client, accounts.GetOpts{})
	if _, ok := result.Err.(gophercloud.ErrDefault404); ok {
		//Swift Account does not exist. This is expected esp. if account_auto_create is disabled
		util.LogDebug("Swift Account %s does not exist", projectUUID)
		return nil
	}
	return &result
}

//Issue a POST request to the account with own headers
func updateAccount(c *gophercloud.ServiceClient, headers map[string]string) (*http.Response, error) {
	return c.Request("POST", c.Endpoint, &gophercloud.RequestOpts{
		MoreHeaders: headers,
		OkCodes:     []int{200, 204},
	})
}

//Issue a PUT request to the account with own headers
func putAccount(c *gophercloud.ServiceClient, headers map[string]string) (*http.Response, error) {
	return c.Request("PUT", c.Endpoint, &gophercloud.RequestOpts{
		MoreHeaders: headers,
		OkCodes:     []int{201},
	})
}
<commit_msg>style<commit_after>/*******************************************************************************
*
* Copyright 2017 SAP SE
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You should have received a copy of the License along with this
* program. If not, 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 plugins

import (
	"net/http"
	"regexp"
	"strconv"

	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack"
	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/accounts"
	"github.com/sapcc/limes/pkg/limes"
	"github.com/sapcc/limes/pkg/util"
)

type swiftPlugin struct{}

var swiftResources = []limes.ResourceInfo{
	limes.ResourceInfo{
		Name: "capacity",
		Unit: limes.UnitBytes,
	},
}

//TODO Make Auth prefix configurable
var urlRegex = regexp.MustCompile("(v1/AUTH_)[a-zA-Z0-9]+")

func init() {
	limes.RegisterQuotaPlugin(&swiftPlugin{})
}

//ServiceType implements the limes.Plugin interface.
func (p *swiftPlugin) ServiceType() string {
	return "object-store"
}

//Resources implements the limes.Plugin interface.
func (p *swiftPlugin) Resources() []limes.ResourceInfo {
	return swiftResources
}

func (p *swiftPlugin) Client(driver limes.Driver) (*gophercloud.ServiceClient, error) {
	return openstack.NewObjectStorageV1(driver.Client(),
		gophercloud.EndpointOpts{Availability: gophercloud.AvailabilityPublic},
	)
}

//Scrape implements the limes.Plugin interface.
func (p *swiftPlugin) Scrape(driver limes.Driver, domainUUID, projectUUID string) (map[string]limes.ResourceData, error) {
	client, err := p.projectClient(driver, projectUUID)
	if err != nil {
		return nil, err
	}

	//Get account metadata
	account := getAccount(client, projectUUID)
	if account == nil {
		//Swift account does not exist, but the keystone project
		return map[string]limes.ResourceData{
			"capacity": limes.ResourceData{
				Quota: 0,
				Usage: 0,
			},
		}, nil
	} else if account.Err != nil {
		return nil, err
	}

	//Extract quota, if set
	var quota int64 = -1
	quotaHeader := account.Header.Get("X-Account-Meta-Quota-Bytes")
	if quotaHeader != "" {
		quota, _ = strconv.ParseInt(quotaHeader, 10, 64)
	}

	//Extract usage
	var usage int64
	usageHeader := account.Header.Get("X-Account-Bytes-Used")
	if usageHeader != "" {
		usage, _ = strconv.ParseInt(usageHeader, 10, 64)
	}

	util.LogDebug("Swift Account %s: quota '%d' - usage '%d'", projectUUID, quota, usage)
	return map[string]limes.ResourceData{
		"capacity": limes.ResourceData{
			Quota: quota,
			Usage: uint64(usage),
		},
	}, nil
}

//SetQuota implements the limes.Plugin interface.
func (p *swiftPlugin) SetQuota(driver limes.Driver, domainUUID, projectUUID string, quotas map[string]uint64) error {
	client, err := p.projectClient(driver, projectUUID)
	if err != nil {
		return err
	}

	headers := map[string]string{
		"X-Account-Meta-Quota-Bytes": string(quotas["capacity"]),
		//this header brought to you by https://github.com/sapcc/swift-addons
		"X-Account-Project-Domain-Id-Override": domainUUID,
	}

	result, err := updateAccount(client, headers)
	if result.StatusCode == http.StatusNotFound && quotas["capacity"] > 0 {
		//account does not exist yet - if there is a non-zero quota, enable it now
		_, err = putAccount(client, headers)
		if err != nil {
			util.LogInfo("Swift Account %s created", projectUUID)
		}
	}
	return err
}

//Get the project scoped cliet with specific storage URL
func (p *swiftPlugin) projectClient(driver limes.Driver, projectUUID string) (*gophercloud.ServiceClient, error) {
	client, err := p.Client(driver)
	if err != nil {
		return nil, err
	}

	//We act as Reseller_Admin here, but cannot use the endpoint url returned from catalogue
	//Replace the resellers project id with the requested one
	client.Endpoint = urlRegex.ReplaceAllString(client.Endpoint, "${1}"+projectUUID)
	util.LogDebug(client.Endpoint)
	return client, nil
}

//Wrapping the accounts.Get because the swift account might not be created if account_auto_create = false
func getAccount(client *gophercloud.ServiceClient, projectUUID string) *accounts.GetResult {
	//Get account metadata
	var result accounts.GetResult
	result = accounts.Get(client, accounts.GetOpts{})
	if _, ok := result.Err.(gophercloud.ErrDefault404); ok {
		//Swift Account does not exist. This is expected esp. if account_auto_create is disabled
		util.LogDebug("Swift Account %s does not exist", projectUUID)
		return nil
	}
	return &result
}

//Issue a POST request to the account with own headers
func updateAccount(c *gophercloud.ServiceClient, headers map[string]string) (*http.Response, error) {
	return c.Request("POST", c.Endpoint, &gophercloud.RequestOpts{
		MoreHeaders: headers,
		OkCodes:     []int{200, 204},
	})
}

//Issue a PUT request to the account with own headers
func putAccount(c *gophercloud.ServiceClient, headers map[string]string) (*http.Response, error) {
	return c.Request("PUT", c.Endpoint, &gophercloud.RequestOpts{
		MoreHeaders: headers,
		OkCodes:     []int{201},
	})
}
<|endoftext|>


<commit_before>package rdb

// #cgo         CFLAGS: -I.
// #cgo         CFLAGS: -I../../third_party/
// #cgo         CFLAGS: -I../../third_party/redis/deps/lua/src/
// #cgo         CFLAGS: -std=c99 -pedantic -O2
// #cgo         CFLAGS: -Wall -W -Wno-missing-field-initializers
// #cgo         CFLAGS: -D_REENTRANT
// #cgo linux   CFLAGS: -D_POSIX_C_SOURCE=199309L
// #cgo        LDFLAGS: -lm
// #cgo linux   CFLAGS: -I../../third_party/jemalloc/include/
// #cgo linux   CFLAGS: -DUSE_JEMALLOC
// #cgo linux  LDFLAGS: -lrt
// #cgo linux  LDFLAGS: -L../../third_party/jemalloc/lib/ -ljemalloc_pic
//
// #include "cgo_redis.h"
//
import "C"

import (
	"io"
	"reflect"
	"strings"
	"time"
	"unsafe"

	"github.com/CodisLabs/codis/pkg/utils/errors"
	"github.com/CodisLabs/codis/pkg/utils/log"
)

const redisServerConfig = `
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-compress-depth 0
list-max-ziplist-size -2
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
rdbchecksum yes
rdbcompression yes
`

func init() {
	var buf = strings.TrimSpace(redisServerConfig)
	var hdr = (*reflect.StringHeader)(unsafe.Pointer(&buf))
	C.initRedisServer(unsafe.Pointer(hdr.Data), C.size_t(hdr.Len))
}

func unsafeCastToLoader(rdb *C.rio) *Loader {
	var l *Loader
	var ptr = uintptr(unsafe.Pointer(rdb)) -
		(unsafe.Offsetof(l.rio) + unsafe.Offsetof(l.rio.rdb))
	return (*Loader)(unsafe.Pointer(ptr))
}

func unsafeCastToSlice(buf unsafe.Pointer, len C.size_t) []byte {
	var hdr = &reflect.SliceHeader{
		Data: uintptr(buf), Len: int(len), Cap: int(len),
	}
	return *(*[]byte)(unsafe.Pointer(hdr))
}

//export cgoRedisRioRead
func cgoRedisRioRead(rdb *C.rio, buf unsafe.Pointer, len C.size_t) C.size_t {
	loader, buffer := unsafeCastToLoader(rdb), unsafeCastToSlice(buf, len)
	return C.size_t(loader.onRead(buffer))
}

//export cgoRedisRioWrite
func cgoRedisRioWrite(rdb *C.rio, buf unsafe.Pointer, len C.size_t) C.size_t {
	loader, buffer := unsafeCastToLoader(rdb), unsafeCastToSlice(buf, len)
	return C.size_t(loader.onWrite(buffer))
}

//export cgoRedisRioTell
func cgoRedisRioTell(rdb *C.rio) C.off_t {
	loader := unsafeCastToLoader(rdb)
	return C.off_t(loader.onTell())
}

//export cgoRedisRioFlush
func cgoRedisRioFlush(rdb *C.rio) C.int {
	loader := unsafeCastToLoader(rdb)
	return C.int(loader.onFlush())
}

//export cgoRedisRioUpdateChecksum
func cgoRedisRioUpdateChecksum(rdb *C.rio, checksum C.uint64_t) {
	loader := unsafeCastToLoader(rdb)
	loader.onUpdateChecksum(uint64(checksum))
}

type redisRio struct {
	rdb C.rio
}

func (r *redisRio) init() {
	C.redisRioInit(&r.rdb)
}

func (r *redisRio) Read(b []byte) error {
	var hdr = (*reflect.SliceHeader)(unsafe.Pointer(&b))
	var ret = C.redisRioRead(&r.rdb, unsafe.Pointer(hdr.Data), C.size_t(hdr.Cap))
	if ret != 0 {
		return errors.Trace(io.ErrUnexpectedEOF)
	}
	return nil
}

func (r *redisRio) LoadLen() uint64 {
	var len C.uint64_t
	var ret = C.redisRioLoadLen(&r.rdb, &len)
	if ret != 0 {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadLen() failed")
	}
	return uint64(len)
}

func (r *redisRio) LoadType() int {
	var typ C.int
	var ret = C.redisRioLoadType(&r.rdb, &typ)
	if ret != 0 {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadType() failed.")
	}
	return int(typ)
}

func (r *redisRio) LoadTime() time.Duration {
	var val C.time_t
	var ret = C.redisRioLoadTime(&r.rdb, &val)
	if ret != 0 {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadTime() failed.")
	}
	return time.Duration(val) * time.Second
}

func (r *redisRio) LoadTimeMillisecond() time.Duration {
	var val C.longlong
	var ret = C.redisRioLoadTimeMillisecond(&r.rdb, &val)
	if ret != 0 {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadTimeMillisecond() failed.")
	}
	return time.Duration(val) * time.Millisecond
}

func (r *redisRio) LoadObject(typ int) *RedisObject {
	var obj = C.redisRioLoadObject(&r.rdb, C.int(typ))
	if obj == nil {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadObject() failed.")
	}
	return &RedisObject{obj}
}

func (r *redisRio) LoadStringObject() *RedisStringObject {
	var obj = C.redisRioLoadStringObject(&r.rdb)
	if obj == nil {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadStringObject() failed.")
	}
	return &RedisStringObject{&RedisObject{obj}}
}

const (
	RDB_VERSION = int64(C.RDB_VERSION)
)

const (
	RDB_OPCODE_AUX           = int(C.RDB_OPCODE_AUX)
	RDB_OPCODE_EOF           = int(C.RDB_OPCODE_EOF)
	RDB_OPCODE_EXPIRETIME    = int(C.RDB_OPCODE_EXPIRETIME)
	RDB_OPCODE_EXPIRETIME_MS = int(C.RDB_OPCODE_EXPIRETIME_MS)
	RDB_OPCODE_RESIZEDB      = int(C.RDB_OPCODE_RESIZEDB)
	RDB_OPCODE_SELECTDB      = int(C.RDB_OPCODE_SELECTDB)

	RDB_TYPE_STRING           = int(C.RDB_TYPE_STRING)
	RDB_TYPE_LIST             = int(C.RDB_TYPE_LIST)
	RDB_TYPE_SET              = int(C.RDB_TYPE_SET)
	RDB_TYPE_ZSET             = int(C.RDB_TYPE_ZSET)
	RDB_TYPE_HASH             = int(C.RDB_TYPE_HASH)
	RDB_TYPE_ZSET_2           = int(C.RDB_TYPE_ZSET_2)
	RDB_TYPE_MODULE           = int(C.RDB_TYPE_MODULE)
	RDB_TYPE_MODULE_2         = int(C.RDB_TYPE_MODULE_2)
	RDB_TYPE_HASH_ZIPMAP      = int(C.RDB_TYPE_HASH_ZIPMAP)
	RDB_TYPE_LIST_ZIPLIST     = int(C.RDB_TYPE_LIST_ZIPLIST)
	RDB_TYPE_SET_INTSET       = int(C.RDB_TYPE_SET_INTSET)
	RDB_TYPE_ZSET_ZIPLIST     = int(C.RDB_TYPE_ZSET_ZIPLIST)
	RDB_TYPE_HASH_ZIPLIST     = int(C.RDB_TYPE_HASH_ZIPLIST)
	RDB_TYPE_LIST_QUICKLIST   = int(C.RDB_TYPE_LIST_QUICKLIST)
	RDB_TYPE_STREAM_LISTPACKS = int(C.RDB_TYPE_STREAM_LISTPACKS)
)

type RedisObject struct {
	obj unsafe.Pointer
}

func (o *RedisObject) DecrRefCount() {
	panic("todo")
}

type RedisStringObject struct {
	*RedisObject
}
<commit_msg>rdb: add constant values<commit_after>package rdb

// #cgo         CFLAGS: -I.
// #cgo         CFLAGS: -I../../third_party/
// #cgo         CFLAGS: -I../../third_party/redis/deps/lua/src/
// #cgo         CFLAGS: -std=c99 -pedantic -O2
// #cgo         CFLAGS: -Wall -W -Wno-missing-field-initializers
// #cgo         CFLAGS: -D_REENTRANT
// #cgo linux   CFLAGS: -D_POSIX_C_SOURCE=199309L
// #cgo        LDFLAGS: -lm
// #cgo linux   CFLAGS: -I../../third_party/jemalloc/include/
// #cgo linux   CFLAGS: -DUSE_JEMALLOC
// #cgo linux  LDFLAGS: -lrt
// #cgo linux  LDFLAGS: -L../../third_party/jemalloc/lib/ -ljemalloc_pic
//
// #include "cgo_redis.h"
//
import "C"

import (
	"fmt"
	"io"
	"reflect"
	"strings"
	"time"
	"unsafe"

	"github.com/CodisLabs/codis/pkg/utils/errors"
	"github.com/CodisLabs/codis/pkg/utils/log"
)

const redisServerConfig = `
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-compress-depth 0
list-max-ziplist-size -2
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
rdbchecksum yes
rdbcompression yes
`

func init() {
	var buf = strings.TrimSpace(redisServerConfig)
	var hdr = (*reflect.StringHeader)(unsafe.Pointer(&buf))
	C.initRedisServer(unsafe.Pointer(hdr.Data), C.size_t(hdr.Len))
}

func unsafeCastToLoader(rdb *C.rio) *Loader {
	var l *Loader
	var ptr = uintptr(unsafe.Pointer(rdb)) -
		(unsafe.Offsetof(l.rio) + unsafe.Offsetof(l.rio.rdb))
	return (*Loader)(unsafe.Pointer(ptr))
}

func unsafeCastToSlice(buf unsafe.Pointer, len C.size_t) []byte {
	var hdr = &reflect.SliceHeader{
		Data: uintptr(buf), Len: int(len), Cap: int(len),
	}
	return *(*[]byte)(unsafe.Pointer(hdr))
}

//export cgoRedisRioRead
func cgoRedisRioRead(rdb *C.rio, buf unsafe.Pointer, len C.size_t) C.size_t {
	loader, buffer := unsafeCastToLoader(rdb), unsafeCastToSlice(buf, len)
	return C.size_t(loader.onRead(buffer))
}

//export cgoRedisRioWrite
func cgoRedisRioWrite(rdb *C.rio, buf unsafe.Pointer, len C.size_t) C.size_t {
	loader, buffer := unsafeCastToLoader(rdb), unsafeCastToSlice(buf, len)
	return C.size_t(loader.onWrite(buffer))
}

//export cgoRedisRioTell
func cgoRedisRioTell(rdb *C.rio) C.off_t {
	loader := unsafeCastToLoader(rdb)
	return C.off_t(loader.onTell())
}

//export cgoRedisRioFlush
func cgoRedisRioFlush(rdb *C.rio) C.int {
	loader := unsafeCastToLoader(rdb)
	return C.int(loader.onFlush())
}

//export cgoRedisRioUpdateChecksum
func cgoRedisRioUpdateChecksum(rdb *C.rio, checksum C.uint64_t) {
	loader := unsafeCastToLoader(rdb)
	loader.onUpdateChecksum(uint64(checksum))
}

type redisRio struct {
	rdb C.rio
}

func (r *redisRio) init() {
	C.redisRioInit(&r.rdb)
}

func (r *redisRio) Read(b []byte) error {
	var hdr = (*reflect.SliceHeader)(unsafe.Pointer(&b))
	var ret = C.redisRioRead(&r.rdb, unsafe.Pointer(hdr.Data), C.size_t(hdr.Cap))
	if ret != 0 {
		return errors.Trace(io.ErrUnexpectedEOF)
	}
	return nil
}

func (r *redisRio) LoadLen() uint64 {
	var len C.uint64_t
	var ret = C.redisRioLoadLen(&r.rdb, &len)
	if ret != 0 {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadLen() failed")
	}
	return uint64(len)
}

func (r *redisRio) LoadType() int {
	var typ C.int
	var ret = C.redisRioLoadType(&r.rdb, &typ)
	if ret != 0 {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadType() failed.")
	}
	return int(typ)
}

func (r *redisRio) LoadTime() time.Duration {
	var val C.time_t
	var ret = C.redisRioLoadTime(&r.rdb, &val)
	if ret != 0 {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadTime() failed.")
	}
	return time.Duration(val) * time.Second
}

func (r *redisRio) LoadTimeMillisecond() time.Duration {
	var val C.longlong
	var ret = C.redisRioLoadTimeMillisecond(&r.rdb, &val)
	if ret != 0 {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadTimeMillisecond() failed.")
	}
	return time.Duration(val) * time.Millisecond
}

func (r *redisRio) LoadObject(typ int) *RedisObject {
	var obj = C.redisRioLoadObject(&r.rdb, C.int(typ))
	if obj == nil {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadObject() failed.")
	}
	return &RedisObject{obj}
}

func (r *redisRio) LoadStringObject() *RedisStringObject {
	var obj = C.redisRioLoadStringObject(&r.rdb)
	if obj == nil {
		log.PanicErrorf(io.ErrUnexpectedEOF, "Read RDB LoadStringObject() failed.")
	}
	return &RedisStringObject{&RedisObject{obj}}
}

const (
	RDB_VERSION = int64(C.RDB_VERSION)
)

const (
	RDB_OPCODE_AUX           = int(C.RDB_OPCODE_AUX)
	RDB_OPCODE_EOF           = int(C.RDB_OPCODE_EOF)
	RDB_OPCODE_EXPIRETIME    = int(C.RDB_OPCODE_EXPIRETIME)
	RDB_OPCODE_EXPIRETIME_MS = int(C.RDB_OPCODE_EXPIRETIME_MS)
	RDB_OPCODE_RESIZEDB      = int(C.RDB_OPCODE_RESIZEDB)
	RDB_OPCODE_SELECTDB      = int(C.RDB_OPCODE_SELECTDB)

	RDB_TYPE_STRING           = int(C.RDB_TYPE_STRING)
	RDB_TYPE_LIST             = int(C.RDB_TYPE_LIST)
	RDB_TYPE_SET              = int(C.RDB_TYPE_SET)
	RDB_TYPE_ZSET             = int(C.RDB_TYPE_ZSET)
	RDB_TYPE_HASH             = int(C.RDB_TYPE_HASH)
	RDB_TYPE_ZSET_2           = int(C.RDB_TYPE_ZSET_2)
	RDB_TYPE_MODULE           = int(C.RDB_TYPE_MODULE)
	RDB_TYPE_MODULE_2         = int(C.RDB_TYPE_MODULE_2)
	RDB_TYPE_HASH_ZIPMAP      = int(C.RDB_TYPE_HASH_ZIPMAP)
	RDB_TYPE_LIST_ZIPLIST     = int(C.RDB_TYPE_LIST_ZIPLIST)
	RDB_TYPE_SET_INTSET       = int(C.RDB_TYPE_SET_INTSET)
	RDB_TYPE_ZSET_ZIPLIST     = int(C.RDB_TYPE_ZSET_ZIPLIST)
	RDB_TYPE_HASH_ZIPLIST     = int(C.RDB_TYPE_HASH_ZIPLIST)
	RDB_TYPE_LIST_QUICKLIST   = int(C.RDB_TYPE_LIST_QUICKLIST)
	RDB_TYPE_STREAM_LISTPACKS = int(C.RDB_TYPE_STREAM_LISTPACKS)
)

const (
	OBJ_STRING = RedisType(C.OBJ_STRING)
	OBJ_LIST   = RedisType(C.OBJ_LIST)
	OBJ_SET    = RedisType(C.OBJ_SET)
	OBJ_ZSET   = RedisType(C.OBJ_ZSET)
	OBJ_HASH   = RedisType(C.OBJ_HASH)
	OBJ_MODULE = RedisType(C.OBJ_MODULE)
	OBJ_STREAM = RedisType(C.OBJ_STREAM)
)

type RedisType int

func (t RedisType) String() string {
	switch t {
	case OBJ_STRING:
		return "OBJ_STRING"
	case OBJ_LIST:
		return "OBJ_LIST"
	case OBJ_SET:
		return "OBJ_SET"
	case OBJ_ZSET:
		return "OBJ_ZSET"
	case OBJ_HASH:
		return "OBJ_HASH"
	case OBJ_MODULE:
		return "OBJ_MODULE"
	case OBJ_STREAM:
		return "OBJ_STREAM"
	}
	return fmt.Sprintf("OBJ_UNKNOWN[%d]", t)
}

const (
	OBJ_ENCODING_RAW        = RedisEncoding(C.OBJ_ENCODING_RAW)
	OBJ_ENCODING_INT        = RedisEncoding(C.OBJ_ENCODING_INT)
	OBJ_ENCODING_HT         = RedisEncoding(C.OBJ_ENCODING_HT)
	OBJ_ENCODING_ZIPMAP     = RedisEncoding(C.OBJ_ENCODING_ZIPMAP)
	OBJ_ENCODING_LINKEDLIST = RedisEncoding(C.OBJ_ENCODING_LINKEDLIST)
	OBJ_ENCODING_ZIPLIST    = RedisEncoding(C.OBJ_ENCODING_ZIPLIST)
	OBJ_ENCODING_INTSET     = RedisEncoding(C.OBJ_ENCODING_INTSET)
	OBJ_ENCODING_SKIPLIST   = RedisEncoding(C.OBJ_ENCODING_SKIPLIST)
	OBJ_ENCODING_EMBSTR     = RedisEncoding(C.OBJ_ENCODING_EMBSTR)
	OBJ_ENCODING_QUICKLIST  = RedisEncoding(C.OBJ_ENCODING_QUICKLIST)
	OBJ_ENCODING_STREAM     = RedisEncoding(C.OBJ_ENCODING_STREAM)
)

type RedisEncoding int

func (t RedisEncoding) String() string {
	switch t {
	case OBJ_ENCODING_RAW:
		return "ENCODING_RAW"
	case OBJ_ENCODING_INT:
		return "ENCODING_INT"
	case OBJ_ENCODING_HT:
		return "ENCODING_HT"
	case OBJ_ENCODING_ZIPMAP:
		return "ENCODING_ZIPMAP"
	case OBJ_ENCODING_LINKEDLIST:
		return "ENCODING_LINKEDLIST"
	case OBJ_ENCODING_ZIPLIST:
		return "ENCODING_ZIPLIST"
	case OBJ_ENCODING_INTSET:
		return "ENCODING_INTSET"
	case OBJ_ENCODING_SKIPLIST:
		return "ENCODING_SKIPLIST"
	case OBJ_ENCODING_EMBSTR:
		return "ENCODING_EMBSTR"
	case OBJ_ENCODING_QUICKLIST:
		return "ENCODING_QUICKLIST"
	case OBJ_ENCODING_STREAM:
		return "ENCODING_STREAM"
	}
	return fmt.Sprintf("ENCODING_UNKNOWN[%d]", t)
}

type RedisObject struct {
	obj unsafe.Pointer
}

func (o *RedisObject) DecrRefCount() {
	panic("todo")
}

type RedisStringObject struct {
	*RedisObject
}
<|endoftext|>


<commit_before>// Command line tool for calling github.com/quchunguang/projecteuler solver.
package main

import (
	"flag"
	"fmt"
	"github.com/quchunguang/projecteuler"
	"os"
	"path"
	"reflect"
	"strings"
)

// Id   option -id target the problem to run.
// N    option -n given N (OPTIONEL).
// File option -f file path to the datafile (OPTIONEL).
type Options struct {
	Id   int
	N    int
	File string
}

// Functions projecteuler.PExxx() will get one or no argument and could be
// any type, return value MUST be int type and no more, holding the answer.
type Solver struct {
	Caller   interface{} // Function handle of solver.
	Arg      interface{} // Default argument used by solver.
	Finished bool        // If the problem had solved.
	Commet   string      // Issues or todo information.
}

// List all solver function handler and default argument.
var Solvers = []Solver{
	// 0 - Hold place, Useless!
	{nil, nil, false, ""},
	{projecteuler.PE1, int(1e3), true, ""},
	{projecteuler.PE2, int(4e6), true, ""},
	{projecteuler.PE3, int(600851475143), true, ""},
	{projecteuler.PE4, nil, true, ""},
	{projecteuler.PE5, int(20), true, ""},
	{projecteuler.PE6, int(100), true, ""},
	{projecteuler.PE7, int(10001), true, ""},
	{projecteuler.PE8, int(13), true, ""},
	{projecteuler.PE9, int(1000), true, ""},
	{projecteuler.PE10, nil, true, ""},
	{projecteuler.PE11, nil, true, ""},
	{projecteuler.PE12, int(500), true, ""},
	{projecteuler.PE13, "p013_bignumbers.txt", true, ""},
	{projecteuler.PE14, int(1e6), true, ""},
	{projecteuler.PE15, int(20), true, ""},
	{projecteuler.PE16, int(1000), true, ""},
	{projecteuler.PE17, int(1000), true, ""},
	{projecteuler.PE18, "p018_path.txt", true, ""},
	{projecteuler.PE19, nil, true, ""},
	{projecteuler.PE20, int(100), true, ""},
	{projecteuler.PE21, int(1e4), true, ""},
	{projecteuler.PE22, "p022_names.txt", true, ""},
	{projecteuler.PE23, nil, true, ""},
	{projecteuler.PE24, int(1e6), true, ""},
	{projecteuler.PE25, int(1000), true, ""},
	{projecteuler.PE26, int(1000), true, ""},
	{projecteuler.PE27, nil, true, ""},
	{projecteuler.PE28, int(1001), true, ""},
	{projecteuler.PE29, int(100), true, ""},
	{projecteuler.PE30, int(5), true, ""},
	{projecteuler.PE31, int(200), true, ""},
	{projecteuler.PE32, nil, true, ""},
	{projecteuler.PE33, nil, true, ""},
	{projecteuler.PE34, nil, true, "this function will never stop"},
	{projecteuler.PE35, int(1e6), true, ""},
	{projecteuler.PE36, int(1e6), true, ""},
	{projecteuler.PE37, nil, true, ""},
	{projecteuler.PE38, nil, true, ""},
	{projecteuler.PE39, int(1000), true, ""},
	{projecteuler.PE40, int(1e6), true, ""},
	{projecteuler.PE41, nil, true, "this function will never stop"},
	{projecteuler.PE42, "p042_words.txt", true, ""},
	{projecteuler.PE43, nil, true, ""},
	{projecteuler.PE44, nil, true, ""},
	{projecteuler.PE45, nil, true, "this function will never stop"},
	{projecteuler.PE46, nil, true, ""},
	{projecteuler.PE47, int(4), true, ""},
	{projecteuler.PE48, int(1000), true, ""},
	{projecteuler.PE49, nil, true, ""},
	{projecteuler.PE50, int(1e6), true, ""},
	{projecteuler.PE51, nil, true, ""},
	{projecteuler.PE52, nil, true, ""},
	{projecteuler.PE53, int(1e6), true, ""},
	{projecteuler.PE54, "p054_poker.txt", true, ""},
	{projecteuler.PE55, int(1e4), true, ""},
	{projecteuler.PE56, int(100), true, ""},
	{projecteuler.PE57, int(1000), true, ""},
	{projecteuler.PE58, nil, true, "Run time about 1 hour"},
	{projecteuler.PE59, "p059_cipher.txt", true, ""},
	{projecteuler.PE60, nil, true, "this function will never stop"},
	{projecteuler.PE61, nil, true, "Only print out answer"},
	{projecteuler.PE62, nil, true, ""},
	{projecteuler.PE63, nil, true, ""},
	{projecteuler.PE64, int(10000), true, ""},
	{projecteuler.PE65, nil, true, ""},
	{projecteuler.PE66, int(1000), true, ""},
	{projecteuler.PE67, "p067_triangle.txt", true, ""},
	{projecteuler.PE68, nil, true, "Only print out answer"},
	{projecteuler.PE69, int(1e6), true, ""},
	{projecteuler.PE70, int(1e7), true, "Run time about 1 hour at 83%"},
	{projecteuler.PE71, int(1e6), true, ""},
	{projecteuler.PE72, int(1e6), true, ""},
	{projecteuler.PE73, int(12000), true, "Run time about 5 minutes"},
	{projecteuler.PE74, int(1e6), true, ""},
	{projecteuler.PE75, int(1500000), true, ""},
	{projecteuler.PE76, int(100), true, ""},
	{projecteuler.PE77, int(5000), true, ""},
	{projecteuler.PE78, int(1e6), true, ""},
	{projecteuler.PE79, "p079_keylog.txt", false, ""},
	{projecteuler.PE80, nil, false, ""},
	{projecteuler.PE81, "p081_matrix.txt", true, ""},
	{projecteuler.PE82, "p082_matrix.txt", true, ""},
	{projecteuler.PE83, "p083_matrix.txt", false, ""},
	{projecteuler.PE84, nil, false, ""},
	{projecteuler.PE85, int(2e6), true, ""},
	{projecteuler.PE86, int(1e2), false, ""},
	{projecteuler.PE87, nil, false, ""},
	{projecteuler.PE88, nil, false, ""},
	{projecteuler.PE89, nil, false, ""},
	{projecteuler.PE90, nil, false, ""},
	{projecteuler.PE91, nil, false, ""},
	{projecteuler.PE92, nil, false, ""},
	{projecteuler.PE93, nil, false, ""},
	{projecteuler.PE94, nil, false, ""},
	{projecteuler.PE95, nil, false, ""},
	{projecteuler.PE96, "p096_sudoku.txt", false, ""},
	{projecteuler.PE97, nil, true, ""},
	{projecteuler.PE98, "p098_words.txt", true, ""},
	{projecteuler.PE99, "p099_base_exp.txt", true, ""},
	{projecteuler.PE100, nil, false, ""},
}

// Call a solver function given problem Id and argument.
// If there is one argument, it could be any type.
// If pass nil, means using default argument given in `Solvers` or the solver
// function need no argument at all.
func Call(Id int, arg interface{}) int {
	if Solvers[Id].Arg != nil && arg == nil {
		arg = Solvers[Id].Arg
		if value, ok := arg.(string); ok {
			// check if the argument is a file
			if strings.HasSuffix(value, ".txt") {
				p := path.Join(os.Getenv("GOPATH"), "src", "github.com/quchunguang/projecteuler/projecteuler", value)
				if !ExistPath(p) {
					fmt.Println("[ERROR] Parameter not a valid path.")
					flag.Usage()
					os.Exit(1)
				}
				arg = p
			}
		}
	}
	f := reflect.ValueOf(Solvers[Id].Caller)
	nArg := f.Type().NumIn()
	if nArg == 0 && arg != nil || nArg == 1 && arg == nil || nArg > 1 {
		fmt.Println("[ERROR] The number of parameters is not adapted.")
		flag.Usage()
		os.Exit(2)
	}
	in := make([]reflect.Value, 1)
	var result []reflect.Value
	if arg != nil {
		in[0] = reflect.ValueOf(arg)
		result = f.Call(in)
	} else {
		result = f.Call(nil)
	}
	return int(result[0].Int())
}

// Check if given pathname is exist and target to a regular file.
func ExistPath(p string) bool {
	finfo, err := os.Stat(p)
	if err != nil {
		fmt.Println("[ERROR] -f: No such file!")
		return false
	}
	if finfo.IsDir() {
		fmt.Println("[ERROR] -f: Not a file!")
		return false
	}
	return true
}

func main() {
	// parse command line arguments
	var opts Options
	flag.IntVar(&opts.Id, "id", 1, "Problem Id.")
	flag.IntVar(&opts.N, "n", -1, "N. Only the first one works in [-n|-f]. (default is the problem setting, depend on problem id given)")
	flag.StringVar(&opts.File, "f", "", "Additional data file. Only the first one works in [-n|-f]. (default target to the data file come with source)")
	h := flag.Bool("h", false, "Usage information. IMPORT: Ensure there is a newline at the end of the file if the file is downloaded from projecteuler.org directly.")
	flag.Parse()

	// process arguments -h
	if *h {
		flag.Usage()
		return
	}

	// check problem id
	if opts.Id < 1 || opts.Id > len(Solvers) || !Solvers[opts.Id].Finished {
		fmt.Println("[ERROR] No such problem or not solved yet!")
		flag.Usage()
		os.Exit(3)
	}

	// process arguments -n -f
	var arg interface{}
	if opts.N != -1 {
		arg = opts.N
	} else if opts.File != "" {
		p := opts.File
		if !path.IsAbs(p) {
			abs, _ := os.Getwd()
			p = path.Join(abs, p)
		}
		if !ExistPath(p) {
			flag.Usage()
			os.Exit(4)
		}
		arg = p
	} else {
		arg = nil
	}

	// calling solver
	answer := Call(opts.Id, arg)
	fmt.Println(answer)
}
<commit_msg>add -about for print the default command line and other infomation with given project Id<commit_after>// Command line tool for calling github.com/quchunguang/projecteuler solver.
package main

import (
	"flag"
	"fmt"
	"github.com/quchunguang/projecteuler"
	"os"
	"path"
	"reflect"
	"strings"
)

const maxId = 521

// Id   option -id target the project to run.
// N    option -n given N (OPTIONEL).
// File option -f file path to the datafile (OPTIONEL).
type Options struct {
	Id   int
	N    int
	File string
}

// Functions projecteuler.PExxx() will get one or no argument and could be
// any type, return value MUST be int type and no more, holding the answer.
type Solver struct {
	Caller   interface{} // Function handle of solver.
	Arg      interface{} // Default argument used by solver.
	Finished bool        // If the project had solved.
	Comment  string      // Issues or todo information.
}

// List all solver function handler and default argument.
var Solvers = []Solver{
	// 0 - Hold place, Useless!
	{nil, nil, false, ""},
	{projecteuler.PE1, int(1e3), true, ""},
	{projecteuler.PE2, int(4e6), true, ""},
	{projecteuler.PE3, int(600851475143), true, ""},
	{projecteuler.PE4, nil, true, ""},
	{projecteuler.PE5, int(20), true, ""},
	{projecteuler.PE6, int(100), true, ""},
	{projecteuler.PE7, int(10001), true, ""},
	{projecteuler.PE8, int(13), true, ""},
	{projecteuler.PE9, int(1000), true, ""},
	{projecteuler.PE10, nil, true, ""},
	{projecteuler.PE11, nil, true, ""},
	{projecteuler.PE12, int(500), true, ""},
	{projecteuler.PE13, "p013_bignumbers.txt", true, ""},
	{projecteuler.PE14, int(1e6), true, ""},
	{projecteuler.PE15, int(20), true, ""},
	{projecteuler.PE16, int(1000), true, ""},
	{projecteuler.PE17, int(1000), true, ""},
	{projecteuler.PE18, "p018_path.txt", true, ""},
	{projecteuler.PE19, nil, true, ""},
	{projecteuler.PE20, int(100), true, ""},
	{projecteuler.PE21, int(1e4), true, ""},
	{projecteuler.PE22, "p022_names.txt", true, ""},
	{projecteuler.PE23, nil, true, ""},
	{projecteuler.PE24, int(1e6), true, ""},
	{projecteuler.PE25, int(1000), true, ""},
	{projecteuler.PE26, int(1000), true, ""},
	{projecteuler.PE27, nil, true, ""},
	{projecteuler.PE28, int(1001), true, ""},
	{projecteuler.PE29, int(100), true, ""},
	{projecteuler.PE30, int(5), true, ""},
	{projecteuler.PE31, int(200), true, ""},
	{projecteuler.PE32, nil, true, ""},
	{projecteuler.PE33, nil, true, ""},
	{projecteuler.PE34, nil, true, "this function will never stop"},
	{projecteuler.PE35, int(1e6), true, ""},
	{projecteuler.PE36, int(1e6), true, ""},
	{projecteuler.PE37, nil, true, ""},
	{projecteuler.PE38, nil, true, ""},
	{projecteuler.PE39, int(1000), true, ""},
	{projecteuler.PE40, int(1e6), true, ""},
	{projecteuler.PE41, nil, true, "this function will never stop"},
	{projecteuler.PE42, "p042_words.txt", true, ""},
	{projecteuler.PE43, nil, true, ""},
	{projecteuler.PE44, nil, true, ""},
	{projecteuler.PE45, nil, true, "this function will never stop"},
	{projecteuler.PE46, nil, true, ""},
	{projecteuler.PE47, int(4), true, ""},
	{projecteuler.PE48, int(1000), true, ""},
	{projecteuler.PE49, nil, true, ""},
	{projecteuler.PE50, int(1e6), true, ""},
	{projecteuler.PE51, nil, true, ""},
	{projecteuler.PE52, nil, true, ""},
	{projecteuler.PE53, int(1e6), true, ""},
	{projecteuler.PE54, "p054_poker.txt", true, ""},
	{projecteuler.PE55, int(1e4), true, ""},
	{projecteuler.PE56, int(100), true, ""},
	{projecteuler.PE57, int(1000), true, ""},
	{projecteuler.PE58, nil, true, "Run time about 1 hour"},
	{projecteuler.PE59, "p059_cipher.txt", true, ""},
	{projecteuler.PE60, nil, true, "this function will never stop"},
	{projecteuler.PE61, nil, true, "Only print out answer"},
	{projecteuler.PE62, nil, true, ""},
	{projecteuler.PE63, nil, true, ""},
	{projecteuler.PE64, int(10000), true, ""},
	{projecteuler.PE65, nil, true, ""},
	{projecteuler.PE66, int(1000), true, ""},
	{projecteuler.PE67, "p067_triangle.txt", true, ""},
	{projecteuler.PE68, nil, true, "Only print out answer"},
	{projecteuler.PE69, int(1e6), true, ""},
	{projecteuler.PE70, int(1e7), true, "Run time about 1 hour at 83%"},
	{projecteuler.PE71, int(1e6), true, ""},
	{projecteuler.PE72, int(1e6), true, ""},
	{projecteuler.PE73, int(12000), true, "Run time about 5 minutes"},
	{projecteuler.PE74, int(1e6), true, ""},
	{projecteuler.PE75, int(1500000), true, ""},
	{projecteuler.PE76, int(100), true, ""},
	{projecteuler.PE77, int(5000), true, ""},
	{projecteuler.PE78, int(1e6), true, ""},
	{projecteuler.PE79, "p079_keylog.txt", false, ""},
	{projecteuler.PE80, nil, false, ""},
	{projecteuler.PE81, "p081_matrix.txt", true, ""},
	{projecteuler.PE82, "p082_matrix.txt", true, ""},
	{projecteuler.PE83, "p083_matrix.txt", false, ""},
	{projecteuler.PE84, nil, false, ""},
	{projecteuler.PE85, int(2e6), true, ""},
	{projecteuler.PE86, int(1e2), false, ""},
	{projecteuler.PE87, nil, false, ""},
	{projecteuler.PE88, nil, false, ""},
	{projecteuler.PE89, nil, false, ""},
	{projecteuler.PE90, nil, false, ""},
	{projecteuler.PE91, nil, false, ""},
	{projecteuler.PE92, nil, false, ""},
	{projecteuler.PE93, nil, false, ""},
	{projecteuler.PE94, nil, false, ""},
	{projecteuler.PE95, nil, false, ""},
	{projecteuler.PE96, "p096_sudoku.txt", false, ""},
	{projecteuler.PE97, nil, true, ""},
	{projecteuler.PE98, "p098_words.txt", true, ""},
	{projecteuler.PE99, "p099_base_exp.txt", true, ""},
	{projecteuler.PE100, nil, false, ""},
}

// Print the default command line with given project Id.
func PrintInfo(Id int) {
	fmt.Println("Project Id:\t\t", Id)

	if Solvers[Id].Arg == nil {
		fmt.Println("Calling Command:\t", "projecteuler -id", Id)
	} else if value, ok := Solvers[Id].Arg.(string); ok {
		value = path.Join(os.Getenv("GOPATH"), "src",
			"github.com/quchunguang/projecteuler/projecteuler", value)
		fmt.Println("Calling Command:\t", "projecteuler -id", Id, "-f", value)
	} else if value, ok := Solvers[Id].Arg.(int); ok {
		fmt.Println("Calling Command:\t", "projecteuler -id", Id, "-n", value)
	} else {
		fmt.Println("[ERROR] BUG, Not supported argument type.")
		os.Exit(5)
	}

	fmt.Println("Comments:\t\t", Solvers[Id].Comment)
	fmt.Println("Solved:\t\t\t", Solvers[Id].Finished)

	fmt.Println("\nTotal Projects:\t\t", maxId)

	totalSolved := 0
	for _, i := range Solvers {
		if i.Finished {
			totalSolved++
		}
	}
	fmt.Println("Total Solved:\t\t", totalSolved)
	fmt.Printf("Finished (%%):\t\t %4.1f\n",
		float32(totalSolved)/float32(maxId)*100.0)
}

// Call a solver function given project Id and argument.
// If there is one argument, it could be any type.
// If pass nil, means using default argument given in `Solvers` or the solver
// function need no argument at all.
func Call(Id int, arg interface{}) int {
	if Solvers[Id].Arg != nil && arg == nil {
		arg = Solvers[Id].Arg
		if value, ok := arg.(string); ok {
			// check if the argument is a file
			if strings.HasSuffix(value, ".txt") {
				p := path.Join(os.Getenv("GOPATH"), "src", "github.com/quchunguang/projecteuler/projecteuler", value)
				if !ExistPath(p) {
					fmt.Println("[ERROR] Parameter not a valid path.")
					flag.Usage()
					os.Exit(1)
				}
				arg = p
			}
		}
	}
	f := reflect.ValueOf(Solvers[Id].Caller)
	nArg := f.Type().NumIn()
	if nArg == 0 && arg != nil || nArg == 1 && arg == nil || nArg > 1 {
		fmt.Println("[ERROR] The number of parameters is not adapted.")
		flag.Usage()
		os.Exit(2)
	}
	in := make([]reflect.Value, 1)
	var result []reflect.Value
	if arg != nil {
		in[0] = reflect.ValueOf(arg)
		result = f.Call(in)
	} else {
		result = f.Call(nil)
	}
	return int(result[0].Int())
}

// Check if given pathname is exist and target to a regular file.
func ExistPath(p string) bool {
	finfo, err := os.Stat(p)
	if err != nil {
		fmt.Println("[ERROR] -f: No such file!")
		return false
	}
	if finfo.IsDir() {
		fmt.Println("[ERROR] -f: Not a file!")
		return false
	}
	return true
}

func main() {
	// parse command line arguments
	var opts Options
	flag.IntVar(&opts.Id, "id", 1, "Project Id.")
	flag.IntVar(&opts.N, "n", -1, "N. Only the first one works in [-n|-f]. (default is the project setting, depend on project id given)")
	flag.StringVar(&opts.File, "f", "", "Additional data file. Only the first one works in [-n|-f]. (default target to the data file come with source)")
	help := flag.Bool("h", false, "Usage information. IMPORT: Ensure there is a newline at the end of the file if the file is downloaded from projecteuler.org directly.")
	about := flag.Bool("about", false, "Print the default command line with given project Id.")
	flag.Parse()

	// process arguments -h
	if *help {
		flag.Usage()
		return
	}

	// check project id
	if opts.Id < 1 || opts.Id >= len(Solvers) || !Solvers[opts.Id].Finished {
		fmt.Println("[ERROR] No such project Id or net solved yet!")
		os.Exit(3)
	}

	// process argument -about
	if *about {
		PrintInfo(opts.Id)
		return
	}

	// process arguments -n -f
	var arg interface{}
	if opts.N != -1 {
		arg = opts.N
	} else if opts.File != "" {
		p := opts.File
		if !path.IsAbs(p) {
			abs, _ := os.Getwd()
			p = path.Join(abs, p)
		}
		if !ExistPath(p) {
			flag.Usage()
			os.Exit(4)
		}
		arg = p
	} else {
		arg = nil
	}

	// calling solver
	answer := Call(opts.Id, arg)
	fmt.Println(answer)
}
<|endoftext|>


<commit_before>/*
Copyright 2012 The Camlistore 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 server

import (
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"strings"

	"camlistore.org/pkg/auth"
	"camlistore.org/pkg/blobserver"
	"camlistore.org/pkg/httputil"
	"camlistore.org/pkg/jsonconfig"
	"camlistore.org/pkg/osutil"
)

var ignoredFields = map[string]bool{
	"gallery":     true,
	"blog":        true,
	"memIndex":    true,
	"replicateTo": true,
}

// SetupHandler handles serving the wizard setup page.
type SetupHandler struct {
	config jsonconfig.Obj
}

func init() {
	blobserver.RegisterHandlerConstructor("setup", newSetupFromConfig)
}

func newSetupFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, err error) {
	wizard := &SetupHandler{config: conf}
	return wizard, nil
}

func printWizard(i interface{}) (s string) {
	switch ei := i.(type) {
	case []string:
		for _, v := range ei {
			s += printWizard(v) + ","
		}
		s = strings.TrimRight(s, ",")
	case []interface{}:
		for _, v := range ei {
			s += printWizard(v) + ","
		}
		s = strings.TrimRight(s, ",")
	default:
		return fmt.Sprintf("%v", i)
	}
	return s
}

// TODO(mpl): probably not needed anymore. check later and remove.
// Flatten all published entities as lists and move them at the root
// of the conf, to have them displayed individually by the template
func flattenPublish(config jsonconfig.Obj) error {
	gallery := []string{}
	blog := []string{}
	config["gallery"] = gallery
	config["blog"] = blog
	published, ok := config["publish"]
	if !ok {
		delete(config, "publish")
		return nil
	}
	pubObj, ok := published.(map[string]interface{})
	if !ok {
		return fmt.Errorf("Was expecting a map[string]interface{} for \"publish\", got %T", published)
	}
	for k, v := range pubObj {
		pub, ok := v.(map[string]interface{})
		if !ok {
			return fmt.Errorf("Was expecting a map[string]interface{} for %s, got %T", k, pub)
		}
		template, rootPermanode, style := "", "", ""
		for pk, pv := range pub {
			val, ok := pv.(string)
			if !ok {
				return fmt.Errorf("Was expecting a string for %s, got %T", pk, pv)
			}
			switch pk {
			case "template":
				template = val
			case "rootPermanode":
				rootPermanode = val
			case "style":
				style = val
			default:
				return fmt.Errorf("Unknown key %q in %s", pk, k)
			}
		}
		if template == "" || rootPermanode == "" {
			return fmt.Errorf("missing \"template\" key or \"rootPermanode\" key in %s", k)
		}
		obj := []string{k, rootPermanode, style}
		config[template] = obj
	}

	delete(config, "publish")
	return nil
}

func sendWizard(rw http.ResponseWriter, req *http.Request, hasChanged bool) {
	config, err := jsonconfig.ReadFile(osutil.UserServerConfigPath())
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}

	err = flattenPublish(config)
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}

	funcMap := template.FuncMap{
		"printWizard": printWizard,
		"showField": func(inputName string) bool {
			if _, ok := ignoredFields[inputName]; ok {
				return false
			}
			return true
		},
	}

	body := `
	<form id="WizardForm" action="" method="post" enctype="multipart/form-data">

	{{range $k,$v := .}}{{if showField $k}}
{{printf "%v" $k}}	<input type="text" size="30" name ="{{printf "%v" $k}}" value="{{printWizard $v}}" >
{{end}}{{end}}

	<input type="submit" form="WizardForm" value="Save"> (Will restart server.)</form>`

	if hasChanged {
		body += `<p> Configuration succesfully rewritten </p>`
	}

	tmpl, err := template.New("wizard").Funcs(funcMap).Parse(topWizard + body + bottomWizard)
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}
	err = tmpl.Execute(rw, config)
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}
}

func rewriteConfig(config *jsonconfig.Obj, configfile string) error {
	b, err := json.MarshalIndent(*config, "", "	")
	if err != nil {
		return err
	}
	s := string(b)
	f, err := os.Create(configfile)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = f.WriteString(s)
	return err
}

// TODO(mpl): use XRRF
func handleSetupChange(rw http.ResponseWriter, req *http.Request) {
	hilevelConf, err := jsonconfig.ReadFile(osutil.UserServerConfigPath())
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}

	hasChanged := false
	var el interface{}
	publish := jsonconfig.Obj{}
	for k, v := range req.Form {
		if _, ok := hilevelConf[k]; !ok {
			if k != "gallery" && k != "blog" {
				continue
			}
		}

		switch k {
		case "https", "shareHandler":
			b, err := strconv.ParseBool(v[0])
			if err != nil {
				httputil.ServeError(rw, req, fmt.Errorf("%v field expects a boolean value", k))
			}
			el = b
		default:
			el = v[0]
		}
		if reflect.DeepEqual(hilevelConf[k], el) {
			continue
		}
		hasChanged = true
		hilevelConf[k] = el
	}
	// "publish" wasn't checked yet
	if !reflect.DeepEqual(hilevelConf["publish"], publish) {
		hilevelConf["publish"] = publish
		hasChanged = true
	}

	if hasChanged {
		err = rewriteConfig(&hilevelConf, osutil.UserServerConfigPath())
		if err != nil {
			httputil.ServeError(rw, req, err)
			return
		}
	}
	sendWizard(rw, req, hasChanged)
	return
}

func (sh *SetupHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if !auth.IsLocalhost(req) {
		fmt.Fprintf(rw,
			"Setup only allowed from localhost"+
				"
Back
"+
				"\n")
		return
	}
	if req.Method == "POST" {
		err := req.ParseMultipartForm(10e6)
		if err != nil {
			httputil.ServeError(rw, req, err)
			return
		}
		if len(req.Form) > 0 {
			handleSetupChange(rw, req)
			err = osutil.RestartProcess()
			if err != nil {
				log.Fatal("Failed to restart: " + err.Error())
			}
		}
	}

	sendWizard(rw, req, false)
}
<commit_msg>wizard: added xsrf protection<commit_after>/*
Copyright 2012 The Camlistore 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 server

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"strings"

	"camlistore.org/pkg/auth"
	"camlistore.org/pkg/blobserver"
	"camlistore.org/pkg/httputil"
	"camlistore.org/pkg/jsonconfig"
	"camlistore.org/pkg/osutil"

	"camlistore.org/third_party/code.google.com/p/xsrftoken"
)

var ignoredFields = map[string]bool{
	"gallery":     true,
	"blog":        true,
	"memIndex":    true,
	"replicateTo": true,
}

// SetupHandler handles serving the wizard setup page.
type SetupHandler struct {
	config jsonconfig.Obj
}

func init() {
	blobserver.RegisterHandlerConstructor("setup", newSetupFromConfig)
}

func newSetupFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (h http.Handler, err error) {
	wizard := &SetupHandler{config: conf}
	return wizard, nil
}

func printWizard(i interface{}) (s string) {
	switch ei := i.(type) {
	case []string:
		for _, v := range ei {
			s += printWizard(v) + ","
		}
		s = strings.TrimRight(s, ",")
	case []interface{}:
		for _, v := range ei {
			s += printWizard(v) + ","
		}
		s = strings.TrimRight(s, ",")
	default:
		return fmt.Sprintf("%v", i)
	}
	return s
}

// TODO(mpl): probably not needed anymore. check later and remove.
// Flatten all published entities as lists and move them at the root
// of the conf, to have them displayed individually by the template
func flattenPublish(config jsonconfig.Obj) error {
	gallery := []string{}
	blog := []string{}
	config["gallery"] = gallery
	config["blog"] = blog
	published, ok := config["publish"]
	if !ok {
		delete(config, "publish")
		return nil
	}
	pubObj, ok := published.(map[string]interface{})
	if !ok {
		return fmt.Errorf("Was expecting a map[string]interface{} for \"publish\", got %T", published)
	}
	for k, v := range pubObj {
		pub, ok := v.(map[string]interface{})
		if !ok {
			return fmt.Errorf("Was expecting a map[string]interface{} for %s, got %T", k, pub)
		}
		template, rootPermanode, style := "", "", ""
		for pk, pv := range pub {
			val, ok := pv.(string)
			if !ok {
				return fmt.Errorf("Was expecting a string for %s, got %T", pk, pv)
			}
			switch pk {
			case "template":
				template = val
			case "rootPermanode":
				rootPermanode = val
			case "style":
				style = val
			default:
				return fmt.Errorf("Unknown key %q in %s", pk, k)
			}
		}
		if template == "" || rootPermanode == "" {
			return fmt.Errorf("missing \"template\" key or \"rootPermanode\" key in %s", k)
		}
		obj := []string{k, rootPermanode, style}
		config[template] = obj
	}

	delete(config, "publish")
	return nil
}

var serverKey = func() string {
	var b [20]byte
	rand.Read(b[:])
	return string(b[:])
}()

func sendWizard(rw http.ResponseWriter, req *http.Request, hasChanged bool) {
	config, err := jsonconfig.ReadFile(osutil.UserServerConfigPath())
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}

	err = flattenPublish(config)
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}

	funcMap := template.FuncMap{
		"printWizard": printWizard,
		"showField": func(inputName string) bool {
			if _, ok := ignoredFields[inputName]; ok {
				return false
			}
			return true
		},
		"genXSRF": func() string {
			return xsrftoken.Generate(serverKey, "user", "wizardSave")
		},
	}

	body := `
	<form id="WizardForm" method="POST" enctype="multipart/form-data">

	{{range $k,$v := .}}{{if showField $k}}
{{printf "%v" $k}}	<input type="text" size="30" name ="{{printf "%v" $k}}" value="{{printWizard $v}}" >
{{end}}{{end}}

	<input type="hidden" name="token" value="{{genXSRF}}">
	<input type="submit" form="WizardForm" value="Save"> (Will restart server.)</form>`

	if hasChanged {
		body += `<p> Configuration succesfully rewritten </p>`
	}

	tmpl, err := template.New("wizard").Funcs(funcMap).Parse(topWizard + body + bottomWizard)
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}
	err = tmpl.Execute(rw, config)
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}
}

func rewriteConfig(config *jsonconfig.Obj, configfile string) error {
	b, err := json.MarshalIndent(*config, "", "	")
	if err != nil {
		return err
	}
	s := string(b)
	f, err := os.Create(configfile)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = f.WriteString(s)
	return err
}

func handleSetupChange(rw http.ResponseWriter, req *http.Request) {
	hilevelConf, err := jsonconfig.ReadFile(osutil.UserServerConfigPath())
	if err != nil {
		httputil.ServeError(rw, req, err)
		return
	}
	if !xsrftoken.Valid(req.FormValue("token"), serverKey, "user", "wizardSave") {
		http.Error(rw, "Form expired. Press back and reload form.", http.StatusBadRequest)
		log.Printf("invalid xsrf token=%q", req.FormValue("token"))
		return
	}

	hasChanged := false
	var el interface{}
	publish := jsonconfig.Obj{}
	for k, v := range req.Form {
		if _, ok := hilevelConf[k]; !ok {
			if k != "gallery" && k != "blog" {
				continue
			}
		}

		switch k {
		case "https", "shareHandler":
			b, err := strconv.ParseBool(v[0])
			if err != nil {
				httputil.ServeError(rw, req, fmt.Errorf("%v field expects a boolean value", k))
			}
			el = b
		default:
			el = v[0]
		}
		if reflect.DeepEqual(hilevelConf[k], el) {
			continue
		}
		hasChanged = true
		hilevelConf[k] = el
	}
	// "publish" wasn't checked yet
	if !reflect.DeepEqual(hilevelConf["publish"], publish) {
		hilevelConf["publish"] = publish
		hasChanged = true
	}

	if hasChanged {
		err = rewriteConfig(&hilevelConf, osutil.UserServerConfigPath())
		if err != nil {
			httputil.ServeError(rw, req, err)
			return
		}
		err = osutil.RestartProcess()
		if err != nil {
			log.Fatal("Failed to restart: " + err.Error())
			http.Error(rw, "Failed to restart process", 500)
			return
		}
	}
	sendWizard(rw, req, hasChanged)
}

func (sh *SetupHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if !auth.IsLocalhost(req) {
		fmt.Fprintf(rw,
			"Setup only allowed from localhost"+
				"
Back
"+
				"\n")
		return
	}
	if req.Method == "POST" {
		err := req.ParseMultipartForm(10e6)
		if err != nil {
			httputil.ServeError(rw, req, err)
			return
		}
		if len(req.Form) > 0 {
			handleSetupChange(rw, req)
		}
		return
	}

	sendWizard(rw, req, false)
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"
	"strings"

	"github.com/dtan4/paus-frontend/store"
	"github.com/pkg/errors"
)

func Create(etcd *store.Etcd, username, appName string) error {
	if err := etcd.Mkdir("/paus/users/" + username + "/apps/" + appName); err != nil {
		return errors.Wrap(err, fmt.Sprintf("Failed to create app. username: %s, appName: %s", username, appName))
	}

	for _, resource := range []string{"build-args", "envs", "revisions"} {
		if err := etcd.Mkdir("/paus/users/" + username + "/apps/" + appName + "/" + resource); err != nil {
			return errors.Wrap(
				err,
				fmt.Sprintf("Failed to create app resource. username: %s, appName: %s, resource: %s", username, appName, resource),
			)
		}
	}

	return nil
}

func Exists(etcd *store.Etcd, username, appName string) bool {
	return etcd.HasKey("/paus/users/" + username + "/apps/" + appName)
}

func List(etcd *store.Etcd, username string) ([]string, error) {
	apps, err := etcd.List("/paus/users/"+username+"/apps/", true)

	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Failed to list up apps. username: %s", username))
	}

	result := make([]string, 0)

	for _, app := range apps {
		appName := strings.Replace(app, "/paus/users/"+username+"/apps/", "", 1)
		result = append(result, appName)
	}

	return result, nil
}

func URL(uriScheme, identifier, baseDomain string) string {
	return strings.ToLower(uriScheme + "://" + identifier + "." + baseDomain)
}

func URLs(etcd *store.Etcd, uriScheme, baseDomain, username, appName string) ([]string, error) {
	revisions, err := etcd.List("/paus/users/"+username+"/apps/"+appName+"/revisions/", true)

	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Failed to list up app URLs. username: %s, appName: %s", username, appName))
	}

	result := make([]string, 0)

	for _, revision := range revisions {
		revision := strings.Replace(revision, "/paus/users/"+username+"/apps/"+appName+"/revisions/", "", 1)
		identifier := username + "-" + appName + "-" + revision[0:8]
		result = append(result, URL(uriScheme, identifier, baseDomain))
	}

	return result, nil
}

func LatestAppURLOfUser(uriScheme, baseDomain, username, appName string) string {
	identifier := username + "-" + appName

	return URL(uriScheme, identifier, baseDomain)
}
<commit_msg>Use deployments instead of revisions<commit_after>package app

import (
	"fmt"
	"strings"

	"github.com/dtan4/paus-frontend/store"
	"github.com/pkg/errors"
)

func Create(etcd *store.Etcd, username, appName string) error {
	if err := etcd.Mkdir("/paus/users/" + username + "/apps/" + appName); err != nil {
		return errors.Wrap(err, fmt.Sprintf("Failed to create app. username: %s, appName: %s", username, appName))
	}

	for _, resource := range []string{"build-args", "envs", "deployments"} {
		if err := etcd.Mkdir("/paus/users/" + username + "/apps/" + appName + "/" + resource); err != nil {
			return errors.Wrap(
				err,
				fmt.Sprintf("Failed to create app resource. username: %s, appName: %s, resource: %s", username, appName, resource),
			)
		}
	}

	return nil
}

func Exists(etcd *store.Etcd, username, appName string) bool {
	return etcd.HasKey("/paus/users/" + username + "/apps/" + appName)
}

func List(etcd *store.Etcd, username string) ([]string, error) {
	apps, err := etcd.List("/paus/users/"+username+"/apps/", true)

	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Failed to list up apps. username: %s", username))
	}

	result := make([]string, 0)

	for _, app := range apps {
		appName := strings.Replace(app, "/paus/users/"+username+"/apps/", "", 1)
		result = append(result, appName)
	}

	return result, nil
}

func URL(uriScheme, identifier, baseDomain string) string {
	return strings.ToLower(uriScheme + "://" + identifier + "." + baseDomain)
}

func URLs(etcd *store.Etcd, uriScheme, baseDomain, username, appName string) ([]string, error) {
	deployments, err := etcd.List("/paus/users/"+username+"/apps/"+appName+"/deployments/", true)

	if err != nil {
		return nil, errors.Wrap(err, fmt.Sprintf("Failed to list up app URLs. username: %s, appName: %s", username, appName))
	}

	result := make([]string, 0)

	for _, deployment := range deployments {
		revision, err := etcd.Get(deployment)

		if err != nil {
			return nil, errors.Wrap(err, "Failed to list up URL.")
		}

		identifier := username + "-" + appName + "-" + revision[0:8]
		result = append(result, URL(uriScheme, identifier, baseDomain))
	}

	return result, nil
}

func LatestAppURLOfUser(uriScheme, baseDomain, username, appName string) string {
	identifier := username + "-" + appName

	return URL(uriScheme, identifier, baseDomain)
}
<|endoftext|>