<commit_before>// vagrant implements the packer.PostProcessor interface and adds a
// post-processor that turns artifacts of known builders into Vagrant
// boxes.
package vagrant

import (
	"fmt"
	"github.com/mitchellh/mapstructure"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
	"log"
)

var builtins = map[string]string{
	"mitchellh.amazonebs":  "aws",
	"mitchellh.virtualbox": "virtualbox",
	"mitchellh.vmware":     "vmware",
}

type Config struct {
	common.PackerConfig `mapstructure:",squash"`

	OutputPath string `mapstructure:"output"`
}

type PostProcessor struct {
	config     Config
	premade    map[string]packer.PostProcessor
	rawConfigs []interface{}
}

func (p *PostProcessor) Configure(raws ...interface{}) error {
	// Store the raw configs for usage later
	p.rawConfigs = raws

	md, err := common.DecodeConfig(&p.config, raws...)
	if err != nil {
		return err
	}

	tpl, err := packer.NewConfigTemplate()
	if err != nil {
		return err
	}
	tpl.UserVars = p.config.PackerUserVars

	// Accumulate any errors
	errs := common.CheckUnusedConfig(md)

	ppExtraConfig := make(map[string]interface{})
	if p.config.OutputPath == "" {
		p.config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box"
		ppExtraConfig["output"] = p.config.OutputPath
	}

	//	_, err := template.New("output").Parse(p.config.OutputPath)
	if err := tpl.Validate(p.config.OutputPath); err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Error parsing output template: %s", err))
		return errs
	}

	// Store the extra configuration for post-processors
	p.rawConfigs = append(p.rawConfigs, ppExtraConfig)

	// TODO(mitchellh): Properly handle multiple raw configs
	var mapConfig map[string]interface{}
	if err := mapstructure.Decode(raws[0], &mapConfig); err != nil {
		return err
	}

	p.premade = make(map[string]packer.PostProcessor)
	errors := make([]error, 0)
	for k, raw := range mapConfig {
		pp := keyToPostProcessor(k)
		if pp == nil {
			continue
		}

		// Create the proper list of configurations
		ppConfigs := make([]interface{}, 0, len(p.rawConfigs)+1)
		copy(ppConfigs, p.rawConfigs)
		ppConfigs = append(ppConfigs, raw)

		if err := pp.Configure(ppConfigs...); err != nil {
			errors = append(errors, err)
		}

		p.premade[k] = pp
	}

	if len(errors) > 0 {
		return &packer.MultiError{errors}
	}

	return nil
}

func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
	ppName, ok := builtins[artifact.BuilderId()]
	if !ok {
		return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId())
	}

	// Use the premade PostProcessor if we have one. Otherwise, we
	// create it and configure it here.
	pp, ok := p.premade[ppName]
	if !ok {
		log.Printf("Premade post-processor for '%s' not found. Creating.", ppName)
		pp = keyToPostProcessor(ppName)
		if pp == nil {
			return nil, false, fmt.Errorf("Vagrant box post-processor not found: %s", ppName)
		}

		if err := pp.Configure(p.rawConfigs...); err != nil {
			return nil, false, err
		}
	}

	ui.Say(fmt.Sprintf("Creating Vagrant box for '%s' provider", ppName))
	return pp.PostProcess(ui, artifact)
}

func keyToPostProcessor(key string) packer.PostProcessor {
	switch key {
	case "aws":
		return new(AWSBoxPostProcessor)
	case "virtualbox":
		return new(VBoxBoxPostProcessor)
	case "vmware":
		return new(VMwareBoxPostProcessor)
	default:
		return nil
	}
}
<commit_msg>post-processor/vagrant: don't error if unused<commit_after>// vagrant implements the packer.PostProcessor interface and adds a
// post-processor that turns artifacts of known builders into Vagrant
// boxes.
package vagrant

import (
	"fmt"
	"github.com/mitchellh/mapstructure"
	"github.com/mitchellh/packer/common"
	"github.com/mitchellh/packer/packer"
	"log"
)

var builtins = map[string]string{
	"mitchellh.amazonebs":  "aws",
	"mitchellh.virtualbox": "virtualbox",
	"mitchellh.vmware":     "vmware",
}

type Config struct {
	common.PackerConfig `mapstructure:",squash"`

	OutputPath string `mapstructure:"output"`
}

type PostProcessor struct {
	config     Config
	premade    map[string]packer.PostProcessor
	rawConfigs []interface{}
}

func (p *PostProcessor) Configure(raws ...interface{}) error {
	// Store the raw configs for usage later
	p.rawConfigs = raws

	_, err := common.DecodeConfig(&p.config, raws...)
	if err != nil {
		return err
	}

	tpl, err := packer.NewConfigTemplate()
	if err != nil {
		return err
	}
	tpl.UserVars = p.config.PackerUserVars

	// Defaults
	ppExtraConfig := make(map[string]interface{})
	if p.config.OutputPath == "" {
		p.config.OutputPath = "packer_{{ .BuildName }}_{{.Provider}}.box"
		ppExtraConfig["output"] = p.config.OutputPath
	}

	// Accumulate any errors
	errs := new(packer.MultiError)
	if err := tpl.Validate(p.config.OutputPath); err != nil {
		errs = packer.MultiErrorAppend(
			errs, fmt.Errorf("Error parsing output template: %s", err))
	}

	// Store the extra configuration for post-processors
	p.rawConfigs = append(p.rawConfigs, ppExtraConfig)

	// TODO(mitchellh): Properly handle multiple raw configs
	var mapConfig map[string]interface{}
	if err := mapstructure.Decode(raws[0], &mapConfig); err != nil {
		errs = packer.MultiErrorAppend(errs,
			fmt.Errorf("Failed to decode config: %s", err))
		return errs
	}

	p.premade = make(map[string]packer.PostProcessor)
	for k, raw := range mapConfig {
		pp := keyToPostProcessor(k)
		if pp == nil {
			continue
		}

		// Create the proper list of configurations
		ppConfigs := make([]interface{}, 0, len(p.rawConfigs)+1)
		copy(ppConfigs, p.rawConfigs)
		ppConfigs = append(ppConfigs, raw)

		if err := pp.Configure(ppConfigs...); err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}

		p.premade[k] = pp
	}

	if len(errs.Errors) > 0 {
		return errs
	}

	return nil
}

func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
	ppName, ok := builtins[artifact.BuilderId()]
	if !ok {
		return nil, false, fmt.Errorf("Unknown artifact type, can't build box: %s", artifact.BuilderId())
	}

	// Use the premade PostProcessor if we have one. Otherwise, we
	// create it and configure it here.
	pp, ok := p.premade[ppName]
	if !ok {
		log.Printf("Premade post-processor for '%s' not found. Creating.", ppName)
		pp = keyToPostProcessor(ppName)
		if pp == nil {
			return nil, false, fmt.Errorf("Vagrant box post-processor not found: %s", ppName)
		}

		if err := pp.Configure(p.rawConfigs...); err != nil {
			return nil, false, err
		}
	}

	ui.Say(fmt.Sprintf("Creating Vagrant box for '%s' provider", ppName))
	return pp.PostProcess(ui, artifact)
}

func keyToPostProcessor(key string) packer.PostProcessor {
	switch key {
	case "aws":
		return new(AWSBoxPostProcessor)
	case "virtualbox":
		return new(VBoxBoxPostProcessor)
	case "vmware":
		return new(VMwareBoxPostProcessor)
	default:
		return nil
	}
}
<|endoftext|>


<commit_before>package analysis

import (
	"fmt"

	"github.com/orange-lang/orange/pkg/ast"
	"github.com/orange-lang/orange/pkg/types"
)

type typeChecker struct {
	ast.DefaultNodeVisitor

	scope    *Scope
	typeInfo *TypeInfo

	firstError error

	Error func(e error)
}

func (v *typeChecker) GetError() error { return v.firstError }

func (v *typeChecker) addError(str string, args ...interface{}) {
	err := fmt.Errorf(str, args...)

	if v.firstError == nil {
		v.firstError = err
	}

	if v.Error != nil {
		v.Error(err)
	}
}

// getType walks a node and looks up its type in the cache if it
// doesn't already exist
func (v *typeChecker) getType(node ast.Node) (types.Type, bool) {
	if ty, ok := v.typeInfo.Types[node]; ok {
		return ty, true
	}

	node.Accept(v)
	ty, ok := v.typeInfo.Types[node]
	return ty, ok
}

func (v *typeChecker) SetType(node ast.Node, ty types.Type) {
	v.typeInfo.Types[node] = ty
}

func (v *typeChecker) VisitBinaryExpr(node *ast.BinaryExpr) {
	lhsType, lhsOk := v.getType(node.LHS)
	rhsType, rhsOk := v.getType(node.RHS)

	if !lhsOk || !rhsOk {
		return
	}

	hadError := false

	reportError := func(str string, args ...interface{}) {
		v.addError(str, args...)
		hadError = true
	}

	if !isBinOp(node.Operation) {
		reportError(InvalidBinOp, node.Operation)
	}

	if isVoidType(lhsType) || isVoidType(rhsType) {
		reportError(BinOpOnVoid)
	}

	if !lhsType.Equals(rhsType, false) {
		reportError(BinOpMismatch)
	}

	if isArithmeticOp(node.Operation) {
		if !isNumericType(lhsType) || !isNumericType(rhsType) {
			reportError(BinOpInvalid, node.Operation, lhsType, rhsType)
		}
	}

	if isLogicalOp(node.Operation) {
		if !isBooleanType(lhsType) || !isBooleanType(rhsType) {
			reportError(BinOpLogicalOpErr, node.Operation)
		}
	}

	if !hadError {
		ty := lhsType.Clone()
		ty.UnsetFlag(types.FlagConst | types.FlagLValue)
		v.SetType(node, ty)
	}
}

func (v *typeChecker) VisitStringExpr(node *ast.StringExpr) {
	v.SetType(node, &types.Array{InnerType: &types.Char{}})
}

func (v *typeChecker) VisitBoolExpr(node *ast.BoolExpr) {
	v.SetType(node, &types.Bool{})
}

func (v *typeChecker) VisitCharExpr(node *ast.CharExpr) {
	v.SetType(node, &types.Char{})
}

func (v *typeChecker) VisitIntExpr(node *ast.IntExpr) {
	v.SetType(node, &types.Int{Signed: true, Size: node.Size})
}

func (v *typeChecker) VisitUIntExpr(node *ast.UIntExpr) {
	v.SetType(node, &types.Int{Signed: false, Size: node.Size})
}

func (v *typeChecker) VisitDoubleExpr(node *ast.DoubleExpr) {
	v.SetType(node, &types.Double{})
}

func (v *typeChecker) VisitFloatExpr(node *ast.FloatExpr) {
	v.SetType(node, &types.Float{})
}

func newTypeChecker(scope *Scope, ti *TypeInfo) *typeChecker {
	return &typeChecker{
		scope:    scope,
		typeInfo: ti,
	}
}
<commit_msg>Rename scope to currentScope in typeChecker<commit_after>package analysis

import (
	"fmt"

	"github.com/orange-lang/orange/pkg/ast"
	"github.com/orange-lang/orange/pkg/types"
)

type typeChecker struct {
	ast.DefaultNodeVisitor

	currentScope *Scope
	typeInfo     *TypeInfo

	firstError error

	Error func(e error)
}

func (v *typeChecker) GetError() error { return v.firstError }

func (v *typeChecker) addError(str string, args ...interface{}) {
	err := fmt.Errorf(str, args...)

	if v.firstError == nil {
		v.firstError = err
	}

	if v.Error != nil {
		v.Error(err)
	}
}

// getType walks a node and looks up its type in the cache if it
// doesn't already exist
func (v *typeChecker) getType(node ast.Node) (types.Type, bool) {
	if ty, ok := v.typeInfo.Types[node]; ok {
		return ty, true
	}

	node.Accept(v)
	ty, ok := v.typeInfo.Types[node]
	return ty, ok
}

func (v *typeChecker) SetType(node ast.Node, ty types.Type) {
	v.typeInfo.Types[node] = ty
}

func (v *typeChecker) VisitBinaryExpr(node *ast.BinaryExpr) {
	lhsType, lhsOk := v.getType(node.LHS)
	rhsType, rhsOk := v.getType(node.RHS)

	if !lhsOk || !rhsOk {
		return
	}

	hadError := false

	reportError := func(str string, args ...interface{}) {
		v.addError(str, args...)
		hadError = true
	}

	if !isBinOp(node.Operation) {
		reportError(InvalidBinOp, node.Operation)
	}

	if isVoidType(lhsType) || isVoidType(rhsType) {
		reportError(BinOpOnVoid)
	}

	if !lhsType.Equals(rhsType, false) {
		reportError(BinOpMismatch)
	}

	if isArithmeticOp(node.Operation) {
		if !isNumericType(lhsType) || !isNumericType(rhsType) {
			reportError(BinOpInvalid, node.Operation, lhsType, rhsType)
		}
	}

	if isLogicalOp(node.Operation) {
		if !isBooleanType(lhsType) || !isBooleanType(rhsType) {
			reportError(BinOpLogicalOpErr, node.Operation)
		}
	}

	if !hadError {
		ty := lhsType.Clone()
		ty.UnsetFlag(types.FlagConst | types.FlagLValue)
		v.SetType(node, ty)
	}
}

func (v *typeChecker) VisitStringExpr(node *ast.StringExpr) {
	v.SetType(node, &types.Array{InnerType: &types.Char{}})
}

func (v *typeChecker) VisitBoolExpr(node *ast.BoolExpr) {
	v.SetType(node, &types.Bool{})
}

func (v *typeChecker) VisitCharExpr(node *ast.CharExpr) {
	v.SetType(node, &types.Char{})
}

func (v *typeChecker) VisitIntExpr(node *ast.IntExpr) {
	v.SetType(node, &types.Int{Signed: true, Size: node.Size})
}

func (v *typeChecker) VisitUIntExpr(node *ast.UIntExpr) {
	v.SetType(node, &types.Int{Signed: false, Size: node.Size})
}

func (v *typeChecker) VisitDoubleExpr(node *ast.DoubleExpr) {
	v.SetType(node, &types.Double{})
}

func (v *typeChecker) VisitFloatExpr(node *ast.FloatExpr) {
	v.SetType(node, &types.Float{})
}

func newTypeChecker(scope *Scope, ti *TypeInfo) *typeChecker {
	return &typeChecker{
		currentScope: scope,
		typeInfo:     ti,
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

package validation

import (
	"encoding/json"
	"fmt"
	"reflect"
	"regexp"
	"strings"

	"github.com/emicklei/go-restful/swagger"
	"github.com/golang/glog"
	apiutil "k8s.io/kubernetes/pkg/api/util"
	"k8s.io/kubernetes/pkg/runtime"
	utilerrors "k8s.io/kubernetes/pkg/util/errors"
	"k8s.io/kubernetes/pkg/util/yaml"
)

type InvalidTypeError struct {
	ExpectedKind reflect.Kind
	ObservedKind reflect.Kind
	FieldName    string
}

func (i *InvalidTypeError) Error() string {
	return fmt.Sprintf("expected type %s, for field %s, got %s", i.ExpectedKind.String(), i.FieldName, i.ObservedKind.String())
}

func NewInvalidTypeError(expected reflect.Kind, observed reflect.Kind, fieldName string) error {
	return &InvalidTypeError{expected, observed, fieldName}
}

// TypeNotFoundError is returned when specified type
// can not found in schema
type TypeNotFoundError string

func (tnfe TypeNotFoundError) Error() string {
	return fmt.Sprintf("couldn't find type: %s", string(tnfe))
}

// Schema is an interface that knows how to validate an API object serialized to a byte array.
type Schema interface {
	ValidateBytes(data []byte) error
}

type NullSchema struct{}

func (NullSchema) ValidateBytes(data []byte) error { return nil }

type SwaggerSchema struct {
	api      swagger.ApiDeclaration
	delegate Schema // For delegating to other api groups
}

func NewSwaggerSchemaFromBytes(data []byte, factory Schema) (Schema, error) {
	schema := &SwaggerSchema{}
	err := json.Unmarshal(data, &schema.api)
	if err != nil {
		return nil, err
	}
	schema.delegate = factory
	return schema, nil
}

// validateList unpacks a list and validate every item in the list.
// It return nil if every item is ok.
// Otherwise it return an error list contain errors of every item.
func (s *SwaggerSchema) validateList(obj map[string]interface{}) []error {
	items, exists := obj["items"]
	if !exists {
		return []error{fmt.Errorf("no items field in %#v", obj)}
	}
	return s.validateItems(items)
}

func (s *SwaggerSchema) validateItems(items interface{}) []error {
	allErrs := []error{}
	itemList, ok := items.([]interface{})
	if !ok {
		return append(allErrs, fmt.Errorf("items isn't a slice"))
	}
	for i, item := range itemList {
		fields, ok := item.(map[string]interface{})
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("items[%d] isn't a map[string]interface{}", i))
			continue
		}
		groupVersion := fields["apiVersion"]
		if groupVersion == nil {
			allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion not set", i))
			continue
		}
		itemVersion, ok := groupVersion.(string)
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion isn't string type", i))
			continue
		}
		if len(itemVersion) == 0 {
			allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion is empty", i))
		}
		kind := fields["kind"]
		if kind == nil {
			allErrs = append(allErrs, fmt.Errorf("items[%d].kind not set", i))
			continue
		}
		itemKind, ok := kind.(string)
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("items[%d].kind isn't string type", i))
			continue
		}
		if len(itemKind) == 0 {
			allErrs = append(allErrs, fmt.Errorf("items[%d].kind is empty", i))
		}
		version := apiutil.GetVersion(itemVersion)
		errs := s.ValidateObject(item, "", version+"."+itemKind)
		if len(errs) >= 1 {
			allErrs = append(allErrs, errs...)
		}
	}

	return allErrs
}

func (s *SwaggerSchema) ValidateBytes(data []byte) error {
	var obj interface{}
	out, err := yaml.ToJSON(data)
	if err != nil {
		return err
	}
	data = out
	if err := json.Unmarshal(data, &obj); err != nil {
		return err
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return fmt.Errorf("error in unmarshaling data %s", string(data))
	}
	groupVersion := fields["apiVersion"]
	if groupVersion == nil {
		return fmt.Errorf("apiVersion not set")
	}
	if _, ok := groupVersion.(string); !ok {
		return fmt.Errorf("apiVersion isn't string type")
	}
	kind := fields["kind"]
	if kind == nil {
		return fmt.Errorf("kind not set")
	}
	if _, ok := kind.(string); !ok {
		return fmt.Errorf("kind isn't string type")
	}
	if strings.HasSuffix(kind.(string), "List") {
		return utilerrors.NewAggregate(s.validateList(fields))
	}
	version := apiutil.GetVersion(groupVersion.(string))
	allErrs := s.ValidateObject(obj, "", version+"."+kind.(string))
	if len(allErrs) == 1 {
		return allErrs[0]
	}
	return utilerrors.NewAggregate(allErrs)
}

func (s *SwaggerSchema) ValidateObject(obj interface{}, fieldName, typeName string) []error {
	allErrs := []error{}
	models := s.api.Models
	model, ok := models.At(typeName)

	// Verify the api version matches.  This is required for nested types with differing api versions because
	// s.api only has schema for 1 api version (the parent object type's version).
	// e.g. an extensions/v1beta1 Template embedding a /v1 Service requires the schema for the extensions/v1beta1
	// api to delegate to the schema for the /v1 api.
	// Only do this for !ok objects so that cross ApiVersion vendored types take precedence.
	if !ok && s.delegate != nil {
		fields, mapOk := obj.(map[string]interface{})
		if !mapOk {
			return append(allErrs, fmt.Errorf("field %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, obj))
		}
		if delegated, err := s.delegateIfDifferentApiVersion(runtime.Unstructured{Object: fields}); delegated {
			if err != nil {
				allErrs = append(allErrs, err)
			}
			return allErrs
		}
	}

	if !ok {
		return append(allErrs, TypeNotFoundError(typeName))
	}
	properties := model.Properties
	if len(properties.List) == 0 {
		// The object does not have any sub-fields.
		return nil
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return append(allErrs, fmt.Errorf("field %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, obj))
	}
	if len(fieldName) > 0 {
		fieldName = fieldName + "."
	}
	// handle required fields
	for _, requiredKey := range model.Required {
		if _, ok := fields[requiredKey]; !ok {
			allErrs = append(allErrs, fmt.Errorf("field %s: is required", requiredKey))
		}
	}
	for key, value := range fields {
		details, ok := properties.At(key)

		// Special case for runtime.RawExtension and runtime.Objects because they always fail to validate
		// This is because the actual values will be of some sub-type (e.g. Deployment) not the expected
		// super-type (RawExtension)
		if s.isGenericArray(details) {
			errs := s.validateItems(value)
			if len(errs) > 0 {
				allErrs = append(allErrs, errs...)
			}
			continue
		}
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("found invalid field %s for %s", key, typeName))
			continue
		}
		if details.Type == nil && details.Ref == nil {
			allErrs = append(allErrs, fmt.Errorf("could not find the type of %s from object: %v", key, details))
		}
		var fieldType string
		if details.Type != nil {
			fieldType = *details.Type
		} else {
			fieldType = *details.Ref
		}
		if value == nil {
			glog.V(2).Infof("Skipping nil field: %s", key)
			continue
		}
		errs := s.validateField(value, fieldName+key, fieldType, &details)
		if len(errs) > 0 {
			allErrs = append(allErrs, errs...)
		}
	}
	return allErrs
}

// delegateIfDifferentApiVersion delegates the validation of an object if its ApiGroup does not match the
// current SwaggerSchema.
// First return value is true if the validation was delegated (by a different ApiGroup SwaggerSchema)
// Second return value is the result of the delegated validation if performed.
func (s *SwaggerSchema) delegateIfDifferentApiVersion(obj runtime.Unstructured) (bool, error) {
	// Never delegate objects in the same ApiVersion or we will get infinite recursion
	if !s.isDifferentApiVersion(obj) {
		return false, nil
	}

	// Convert the object back into bytes so that we can pass it to the ValidateBytes function
	m, err := json.Marshal(obj.Object)
	if err != nil {
		return true, err
	}

	// Delegate validation of this object to the correct SwaggerSchema for its ApiGroup
	return true, s.delegate.ValidateBytes(m)
}

// isDifferentApiVersion Returns true if obj lives in a different ApiVersion than the SwaggerSchema does.
// The SwaggerSchema will not be able to process objects in different ApiVersions unless they are vendored.
func (s *SwaggerSchema) isDifferentApiVersion(obj runtime.Unstructured) bool {
	groupVersion := obj.GetAPIVersion()
	return len(groupVersion) > 0 && s.api.ApiVersion != groupVersion
}

// isGenericArray Returns true if p is an array of generic Objects - either RawExtension or Object.
func (s *SwaggerSchema) isGenericArray(p swagger.ModelProperty) bool {
	return p.DataTypeFields.Type != nil &&
		*p.DataTypeFields.Type == "array" &&
		p.Items != nil &&
		p.Items.Ref != nil &&
		(*p.Items.Ref == "runtime.RawExtension" || *p.Items.Ref == "runtime.Object")
}

// This matches type name in the swagger spec, such as "v1.Binding".
var versionRegexp = regexp.MustCompile(`^v.+\..*`)

func (s *SwaggerSchema) validateField(value interface{}, fieldName, fieldType string, fieldDetails *swagger.ModelProperty) []error {
	allErrs := []error{}
	if reflect.TypeOf(value) == nil {
		return append(allErrs, fmt.Errorf("unexpected nil value for field %v", fieldName))
	}
	// TODO: caesarxuchao: because we have multiple group/versions and objects
	// may reference objects in other group, the commented out way of checking
	// if a filedType is a type defined by us is outdated. We use a hacky way
	// for now.
	// TODO: the type name in the swagger spec is something like "v1.Binding",
	// and the "v1" is generated from the package name, not the groupVersion of
	// the type. We need to fix go-restful to embed the group name in the type
	// name, otherwise we couldn't handle identically named types in different
	// groups correctly.
	if versionRegexp.MatchString(fieldType) {
		// if strings.HasPrefix(fieldType, apiVersion) {
		return s.ValidateObject(value, fieldName, fieldType)
	}
	switch fieldType {
	case "string":
		// Be loose about what we accept for 'string' since we use IntOrString in a couple of places
		_, isString := value.(string)
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isString && !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.String, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "array":
		arr, ok := value.([]interface{})
		if !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		var arrType string
		if fieldDetails.Items.Ref == nil && fieldDetails.Items.Type == nil {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		if fieldDetails.Items.Ref != nil {
			arrType = *fieldDetails.Items.Ref
		} else {
			arrType = *fieldDetails.Items.Type
		}
		for ix := range arr {
			errs := s.validateField(arr[ix], fmt.Sprintf("%s[%d]", fieldName, ix), arrType, nil)
			if len(errs) > 0 {
				allErrs = append(allErrs, errs...)
			}
		}
	case "uint64":
	case "int64":
	case "integer":
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.Int, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "float64":
		if _, ok := value.(float64); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Float64, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "boolean":
		if _, ok := value.(bool); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Bool, reflect.TypeOf(value).Kind(), fieldName))
		}
	// API servers before release 1.3 produce swagger spec with `type: "any"` as the fallback type, while newer servers produce spec with `type: "object"`.
	// We have both here so that kubectl can work with both old and new api servers.
	case "object":
	case "any":
	default:
		return append(allErrs, fmt.Errorf("unexpected type: %v", fieldType))
	}
	return allErrs
}
<commit_msg>Fix pvc label selector validation error<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

package validation

import (
	"encoding/json"
	"fmt"
	"reflect"
	"regexp"
	"strings"

	"github.com/emicklei/go-restful/swagger"
	"github.com/golang/glog"
	apiutil "k8s.io/kubernetes/pkg/api/util"
	"k8s.io/kubernetes/pkg/runtime"
	utilerrors "k8s.io/kubernetes/pkg/util/errors"
	"k8s.io/kubernetes/pkg/util/yaml"
)

type InvalidTypeError struct {
	ExpectedKind reflect.Kind
	ObservedKind reflect.Kind
	FieldName    string
}

func (i *InvalidTypeError) Error() string {
	return fmt.Sprintf("expected type %s, for field %s, got %s", i.ExpectedKind.String(), i.FieldName, i.ObservedKind.String())
}

func NewInvalidTypeError(expected reflect.Kind, observed reflect.Kind, fieldName string) error {
	return &InvalidTypeError{expected, observed, fieldName}
}

// TypeNotFoundError is returned when specified type
// can not found in schema
type TypeNotFoundError string

func (tnfe TypeNotFoundError) Error() string {
	return fmt.Sprintf("couldn't find type: %s", string(tnfe))
}

// Schema is an interface that knows how to validate an API object serialized to a byte array.
type Schema interface {
	ValidateBytes(data []byte) error
}

type NullSchema struct{}

func (NullSchema) ValidateBytes(data []byte) error { return nil }

type SwaggerSchema struct {
	api      swagger.ApiDeclaration
	delegate Schema // For delegating to other api groups
}

func NewSwaggerSchemaFromBytes(data []byte, factory Schema) (Schema, error) {
	schema := &SwaggerSchema{}
	err := json.Unmarshal(data, &schema.api)
	if err != nil {
		return nil, err
	}
	schema.delegate = factory
	return schema, nil
}

// validateList unpacks a list and validate every item in the list.
// It return nil if every item is ok.
// Otherwise it return an error list contain errors of every item.
func (s *SwaggerSchema) validateList(obj map[string]interface{}) []error {
	items, exists := obj["items"]
	if !exists {
		return []error{fmt.Errorf("no items field in %#v", obj)}
	}
	return s.validateItems(items)
}

func (s *SwaggerSchema) validateItems(items interface{}) []error {
	allErrs := []error{}
	itemList, ok := items.([]interface{})
	if !ok {
		return append(allErrs, fmt.Errorf("items isn't a slice"))
	}
	for i, item := range itemList {
		fields, ok := item.(map[string]interface{})
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("items[%d] isn't a map[string]interface{}", i))
			continue
		}
		groupVersion := fields["apiVersion"]
		if groupVersion == nil {
			allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion not set", i))
			continue
		}
		itemVersion, ok := groupVersion.(string)
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion isn't string type", i))
			continue
		}
		if len(itemVersion) == 0 {
			allErrs = append(allErrs, fmt.Errorf("items[%d].apiVersion is empty", i))
		}
		kind := fields["kind"]
		if kind == nil {
			allErrs = append(allErrs, fmt.Errorf("items[%d].kind not set", i))
			continue
		}
		itemKind, ok := kind.(string)
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("items[%d].kind isn't string type", i))
			continue
		}
		if len(itemKind) == 0 {
			allErrs = append(allErrs, fmt.Errorf("items[%d].kind is empty", i))
		}
		version := apiutil.GetVersion(itemVersion)
		errs := s.ValidateObject(item, "", version+"."+itemKind)
		if len(errs) >= 1 {
			allErrs = append(allErrs, errs...)
		}
	}

	return allErrs
}

func (s *SwaggerSchema) ValidateBytes(data []byte) error {
	var obj interface{}
	out, err := yaml.ToJSON(data)
	if err != nil {
		return err
	}
	data = out
	if err := json.Unmarshal(data, &obj); err != nil {
		return err
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return fmt.Errorf("error in unmarshaling data %s", string(data))
	}
	groupVersion := fields["apiVersion"]
	if groupVersion == nil {
		return fmt.Errorf("apiVersion not set")
	}
	if _, ok := groupVersion.(string); !ok {
		return fmt.Errorf("apiVersion isn't string type")
	}
	kind := fields["kind"]
	if kind == nil {
		return fmt.Errorf("kind not set")
	}
	if _, ok := kind.(string); !ok {
		return fmt.Errorf("kind isn't string type")
	}
	if strings.HasSuffix(kind.(string), "List") {
		return utilerrors.NewAggregate(s.validateList(fields))
	}
	version := apiutil.GetVersion(groupVersion.(string))
	allErrs := s.ValidateObject(obj, "", version+"."+kind.(string))
	if len(allErrs) == 1 {
		return allErrs[0]
	}
	return utilerrors.NewAggregate(allErrs)
}

func (s *SwaggerSchema) ValidateObject(obj interface{}, fieldName, typeName string) []error {
	allErrs := []error{}
	models := s.api.Models
	model, ok := models.At(typeName)

	// Verify the api version matches.  This is required for nested types with differing api versions because
	// s.api only has schema for 1 api version (the parent object type's version).
	// e.g. an extensions/v1beta1 Template embedding a /v1 Service requires the schema for the extensions/v1beta1
	// api to delegate to the schema for the /v1 api.
	// Only do this for !ok objects so that cross ApiVersion vendored types take precedence.
	if !ok && s.delegate != nil {
		fields, mapOk := obj.(map[string]interface{})
		if !mapOk {
			return append(allErrs, fmt.Errorf("field %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, obj))
		}
		if delegated, err := s.delegateIfDifferentApiVersion(runtime.Unstructured{Object: fields}); delegated {
			if err != nil {
				allErrs = append(allErrs, err)
			}
			return allErrs
		}
	}

	if !ok {
		return append(allErrs, TypeNotFoundError(typeName))
	}
	properties := model.Properties
	if len(properties.List) == 0 {
		// The object does not have any sub-fields.
		return nil
	}
	fields, ok := obj.(map[string]interface{})
	if !ok {
		return append(allErrs, fmt.Errorf("field %s: expected object of type map[string]interface{}, but the actual type is %T", fieldName, obj))
	}
	if len(fieldName) > 0 {
		fieldName = fieldName + "."
	}
	// handle required fields
	for _, requiredKey := range model.Required {
		if _, ok := fields[requiredKey]; !ok {
			allErrs = append(allErrs, fmt.Errorf("field %s: is required", requiredKey))
		}
	}
	for key, value := range fields {
		details, ok := properties.At(key)

		// Special case for runtime.RawExtension and runtime.Objects because they always fail to validate
		// This is because the actual values will be of some sub-type (e.g. Deployment) not the expected
		// super-type (RawExtension)
		if s.isGenericArray(details) {
			errs := s.validateItems(value)
			if len(errs) > 0 {
				allErrs = append(allErrs, errs...)
			}
			continue
		}
		if !ok {
			allErrs = append(allErrs, fmt.Errorf("found invalid field %s for %s", key, typeName))
			continue
		}
		if details.Type == nil && details.Ref == nil {
			allErrs = append(allErrs, fmt.Errorf("could not find the type of %s from object: %v", key, details))
		}
		var fieldType string
		if details.Type != nil {
			fieldType = *details.Type
		} else {
			fieldType = *details.Ref
		}
		if value == nil {
			glog.V(2).Infof("Skipping nil field: %s", key)
			continue
		}
		errs := s.validateField(value, fieldName+key, fieldType, &details)
		if len(errs) > 0 {
			allErrs = append(allErrs, errs...)
		}
	}
	return allErrs
}

// delegateIfDifferentApiVersion delegates the validation of an object if its ApiGroup does not match the
// current SwaggerSchema.
// First return value is true if the validation was delegated (by a different ApiGroup SwaggerSchema)
// Second return value is the result of the delegated validation if performed.
func (s *SwaggerSchema) delegateIfDifferentApiVersion(obj runtime.Unstructured) (bool, error) {
	// Never delegate objects in the same ApiVersion or we will get infinite recursion
	if !s.isDifferentApiVersion(obj) {
		return false, nil
	}

	// Convert the object back into bytes so that we can pass it to the ValidateBytes function
	m, err := json.Marshal(obj.Object)
	if err != nil {
		return true, err
	}

	// Delegate validation of this object to the correct SwaggerSchema for its ApiGroup
	return true, s.delegate.ValidateBytes(m)
}

// isDifferentApiVersion Returns true if obj lives in a different ApiVersion than the SwaggerSchema does.
// The SwaggerSchema will not be able to process objects in different ApiVersions unless they are vendored.
func (s *SwaggerSchema) isDifferentApiVersion(obj runtime.Unstructured) bool {
	groupVersion := obj.GetAPIVersion()
	return len(groupVersion) > 0 && s.api.ApiVersion != groupVersion
}

// isGenericArray Returns true if p is an array of generic Objects - either RawExtension or Object.
func (s *SwaggerSchema) isGenericArray(p swagger.ModelProperty) bool {
	return p.DataTypeFields.Type != nil &&
		*p.DataTypeFields.Type == "array" &&
		p.Items != nil &&
		p.Items.Ref != nil &&
		(*p.Items.Ref == "runtime.RawExtension" || *p.Items.Ref == "runtime.Object")
}

// This matches type name in the swagger spec, such as "v1.Binding".
var versionRegexp = regexp.MustCompile(`^(v.+|unversioned)\..*`)

func (s *SwaggerSchema) validateField(value interface{}, fieldName, fieldType string, fieldDetails *swagger.ModelProperty) []error {
	allErrs := []error{}
	if reflect.TypeOf(value) == nil {
		return append(allErrs, fmt.Errorf("unexpected nil value for field %v", fieldName))
	}
	// TODO: caesarxuchao: because we have multiple group/versions and objects
	// may reference objects in other group, the commented out way of checking
	// if a filedType is a type defined by us is outdated. We use a hacky way
	// for now.
	// TODO: the type name in the swagger spec is something like "v1.Binding",
	// and the "v1" is generated from the package name, not the groupVersion of
	// the type. We need to fix go-restful to embed the group name in the type
	// name, otherwise we couldn't handle identically named types in different
	// groups correctly.
	if versionRegexp.MatchString(fieldType) {
		// if strings.HasPrefix(fieldType, apiVersion) {
		return s.ValidateObject(value, fieldName, fieldType)
	}
	switch fieldType {
	case "string":
		// Be loose about what we accept for 'string' since we use IntOrString in a couple of places
		_, isString := value.(string)
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isString && !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.String, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "array":
		arr, ok := value.([]interface{})
		if !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		var arrType string
		if fieldDetails.Items.Ref == nil && fieldDetails.Items.Type == nil {
			return append(allErrs, NewInvalidTypeError(reflect.Array, reflect.TypeOf(value).Kind(), fieldName))
		}
		if fieldDetails.Items.Ref != nil {
			arrType = *fieldDetails.Items.Ref
		} else {
			arrType = *fieldDetails.Items.Type
		}
		for ix := range arr {
			errs := s.validateField(arr[ix], fmt.Sprintf("%s[%d]", fieldName, ix), arrType, nil)
			if len(errs) > 0 {
				allErrs = append(allErrs, errs...)
			}
		}
	case "uint64":
	case "int64":
	case "integer":
		_, isNumber := value.(float64)
		_, isInteger := value.(int)
		if !isNumber && !isInteger {
			return append(allErrs, NewInvalidTypeError(reflect.Int, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "float64":
		if _, ok := value.(float64); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Float64, reflect.TypeOf(value).Kind(), fieldName))
		}
	case "boolean":
		if _, ok := value.(bool); !ok {
			return append(allErrs, NewInvalidTypeError(reflect.Bool, reflect.TypeOf(value).Kind(), fieldName))
		}
	// API servers before release 1.3 produce swagger spec with `type: "any"` as the fallback type, while newer servers produce spec with `type: "object"`.
	// We have both here so that kubectl can work with both old and new api servers.
	case "object":
	case "any":
	default:
		return append(allErrs, fmt.Errorf("unexpected type: %v", fieldType))
	}
	return allErrs
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package controller

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"text/template"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/util/iptables"
	k8sexec "k8s.io/utils/exec"
)

const (
	iptablesChain   = "KUBE-KEEPALIVED-VIP"
	keepalivedCfg   = "/etc/keepalived/keepalived.conf"
	haproxyCfg      = "/etc/haproxy/haproxy.cfg"
	keepalivedState = "/var/run/keepalived.state"
)

var (
	keepalivedTmpl = "keepalived.tmpl"
	haproxyTmpl    = "haproxy.tmpl"
)

type keepalived struct {
	iface          string
	ip             string
	netmask        int
	priority       int
	nodes          []string
	neighbors      []string
	useUnicast     bool
	started        bool
	vips           []string
	keepalivedTmpl *template.Template
	haproxyTmpl    *template.Template
	cmd            *exec.Cmd
	ipt            iptables.Interface
	vrid           int
	proxyMode      bool
}

// WriteCfg creates a new keepalived configuration file.
// In case of an error with the generation it returns the error
func (k *keepalived) WriteCfg(svcs []vip) error {
	w, err := os.Create(keepalivedCfg)
	if err != nil {
		return err
	}
	defer w.Close()

	k.vips = getVIPs(svcs)

	conf := make(map[string]interface{})
	conf["iptablesChain"] = iptablesChain
	conf["iface"] = k.iface
	conf["myIP"] = k.ip
	conf["netmask"] = k.netmask
	conf["svcs"] = svcs
	conf["vips"] = k.vips
	conf["nodes"] = k.neighbors
	conf["priority"] = k.priority
	conf["useUnicast"] = k.useUnicast
	conf["vrid"] = k.vrid
	conf["iface"] = k.iface
	conf["proxyMode"] = k.proxyMode
	conf["vipIsEmpty"] = len(k.vips) == 0

	if glog.V(2) {
		b, _ := json.Marshal(conf)
		glog.Infof("%v", string(b))
	}

	err = k.keepalivedTmpl.Execute(w, conf)
	if err != nil {
		return fmt.Errorf("unexpected error creating keepalived.cfg: %v", err)
	}

	if k.proxyMode {
		w, err := os.Create(haproxyCfg)
		if err != nil {
			return err
		}
		defer w.Close()
		err = k.haproxyTmpl.Execute(w, conf)
		if err != nil {
			return fmt.Errorf("unexpected error creating haproxy.cfg: %v", err)
		}
	}

	return nil
}

// getVIPs returns a list of the virtual IP addresses to be used in keepalived
// without duplicates (a service can use more than one port)
func getVIPs(svcs []vip) []string {
	result := []string{}
	for _, svc := range svcs {
		result = appendIfMissing(result, svc.IP)
	}

	return result
}

// Start starts a keepalived process in foreground.
// In case of any error it will terminate the execution with a fatal error
func (k *keepalived) Start() {
	ae, err := k.ipt.EnsureChain(iptables.TableFilter, iptables.Chain(iptablesChain))
	if err != nil {
		glog.Fatalf("unexpected error: %v", err)
	}
	if ae {
		glog.V(2).Infof("chain %v already existed", iptablesChain)
	}

	k.cmd = exec.Command("keepalived",
		"--dont-fork",
		"--log-console",
		"--release-vips",
		"--log-detail",
		"--pid", "/keepalived.pid")

	k.cmd.Stdout = os.Stdout
	k.cmd.Stderr = os.Stderr

	k.cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
		Pgid:    0,
	}

	k.started = true

	if err := k.cmd.Start(); err != nil {
		glog.Errorf("keepalived error: %v", err)
	}

	if err := k.cmd.Wait(); err != nil {
		glog.Fatalf("keepalived error: %v", err)
	}
}

// Reload sends SIGHUP to keepalived to reload the configuration.
func (k *keepalived) Reload() error {
	if !k.started {
		// TODO: add a warning indicating that keepalived is not started?
		return nil
	}

	k.Cleanup()
	glog.Info("reloading keepalived")
	err := syscall.Kill(k.cmd.Process.Pid, syscall.SIGHUP)
	if err != nil {
		return fmt.Errorf("error reloading keepalived: %v", err)
	}

	return nil
}

// Whether keepalived child process is currently running
func (k *keepalived) Healthy() error {
	b, err := ioutil.ReadFile(keepalivedState)
	if err != nil {
		return err
	}

	master := false
	state := strings.TrimSpace(string(b))
	if strings.Contains(state, "MASTER") {
		master = true
	}

	var out bytes.Buffer
	cmd := exec.Command("ip", "-brief", "address", "show", k.iface, "up")
	cmd.Stderr = os.Stderr
	cmd.Stdout = &out
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
		Pgid:    0,
	}

	err = cmd.Run()
	if err != nil {
		return err
	}

	ips := out.String()
	glog.V(3).Infof("Status of %s interface: %s", state, ips)

	for _, vip := range k.vips {
		containsVip := strings.Contains(ips, fmt.Sprintf(" %s/32 ", vip))

		if master && !containsVip {
			return fmt.Errorf("Missing VIP %s on %s", vip, state)
		} else if !master && containsVip {
			return fmt.Errorf("%s should not contain VIP %s", state, vip)
		}
	}

	// All checks successful
        return nil
}

func (k *keepalived) Cleanup() {
	glog.Infof("Cleanup: %s", k.vips)
	for _, vip := range k.vips {
		k.removeVIP(vip)
	}

	err := k.ipt.FlushChain(iptables.TableFilter, iptables.Chain(iptablesChain))
	if err != nil {
		glog.V(2).Infof("unexpected error flushing iptables chain %v: %v", err, iptablesChain)
	}
}

// Stop stop keepalived process
func (k *keepalived) Stop() {
	k.Cleanup()

	err := syscall.Kill(k.cmd.Process.Pid, syscall.SIGTERM)
	if err != nil {
		glog.Errorf("error stopping keepalived: %v", err)
	}
}

func (k *keepalived) removeVIP(vip string) {
	glog.Infof("removing configured VIP %v", vip)
	out, err := k8sexec.New().Command("ip", "addr", "del", vip+"/32", "dev", k.iface).CombinedOutput()
	if err != nil {
		glog.V(2).Infof("Error removing VIP %s: %v\n%s", vip, err, out)
	}
}

func (k *keepalived) loadTemplates() error {
	tmpl, err := template.ParseFiles(keepalivedTmpl)
	if err != nil {
		return err
	}
	k.keepalivedTmpl = tmpl

	tmpl, err = template.ParseFiles(haproxyTmpl)
	if err != nil {
		return err
	}
	k.haproxyTmpl = tmpl

	return nil
}
<commit_msg>Fix race condition crash on startup<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package controller

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"text/template"
	"time"

	"github.com/golang/glog"

	"k8s.io/kubernetes/pkg/util/iptables"
	k8sexec "k8s.io/utils/exec"
)

const (
	iptablesChain   = "KUBE-KEEPALIVED-VIP"
	keepalivedCfg   = "/etc/keepalived/keepalived.conf"
	haproxyCfg      = "/etc/haproxy/haproxy.cfg"
	keepalivedPid   = "/var/run/keepalived.pid"
	keepalivedState = "/var/run/keepalived.state"
	vrrpPid         = "/var/run/vrrp.pid"
)

var (
	keepalivedTmpl = "keepalived.tmpl"
	haproxyTmpl    = "haproxy.tmpl"
)

type keepalived struct {
	iface          string
	ip             string
	netmask        int
	priority       int
	nodes          []string
	neighbors      []string
	useUnicast     bool
	started        bool
	vips           []string
	keepalivedTmpl *template.Template
	haproxyTmpl    *template.Template
	cmd            *exec.Cmd
	ipt            iptables.Interface
	vrid           int
	proxyMode      bool
}

// WriteCfg creates a new keepalived configuration file.
// In case of an error with the generation it returns the error
func (k *keepalived) WriteCfg(svcs []vip) error {
	w, err := os.Create(keepalivedCfg)
	if err != nil {
		return err
	}
	defer w.Close()

	k.vips = getVIPs(svcs)

	conf := make(map[string]interface{})
	conf["iptablesChain"] = iptablesChain
	conf["iface"] = k.iface
	conf["myIP"] = k.ip
	conf["netmask"] = k.netmask
	conf["svcs"] = svcs
	conf["vips"] = k.vips
	conf["nodes"] = k.neighbors
	conf["priority"] = k.priority
	conf["useUnicast"] = k.useUnicast
	conf["vrid"] = k.vrid
	conf["iface"] = k.iface
	conf["proxyMode"] = k.proxyMode
	conf["vipIsEmpty"] = len(k.vips) == 0

	if glog.V(2) {
		b, _ := json.Marshal(conf)
		glog.Infof("%v", string(b))
	}

	err = k.keepalivedTmpl.Execute(w, conf)
	if err != nil {
		return fmt.Errorf("unexpected error creating keepalived.cfg: %v", err)
	}

	if k.proxyMode {
		w, err := os.Create(haproxyCfg)
		if err != nil {
			return err
		}
		defer w.Close()
		err = k.haproxyTmpl.Execute(w, conf)
		if err != nil {
			return fmt.Errorf("unexpected error creating haproxy.cfg: %v", err)
		}
	}

	return nil
}

// getVIPs returns a list of the virtual IP addresses to be used in keepalived
// without duplicates (a service can use more than one port)
func getVIPs(svcs []vip) []string {
	result := []string{}
	for _, svc := range svcs {
		result = appendIfMissing(result, svc.IP)
	}

	return result
}

// Start starts a keepalived process in foreground.
// In case of any error it will terminate the execution with a fatal error
func (k *keepalived) Start() {
	ae, err := k.ipt.EnsureChain(iptables.TableFilter, iptables.Chain(iptablesChain))
	if err != nil {
		glog.Fatalf("unexpected error: %v", err)
	}
	if ae {
		glog.V(2).Infof("chain %v already existed", iptablesChain)
	}

	k.cmd = exec.Command("keepalived",
		"--dont-fork",
		"--log-console",
		"--release-vips",
		"--log-detail")

	k.cmd.Stdout = os.Stdout
	k.cmd.Stderr = os.Stderr

	k.cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
		Pgid:    0,
	}

	k.started = true

	if err := k.cmd.Run(); err != nil {
		glog.Fatalf("Error starting keepalived: %v", err)
	}
}

// Reload sends SIGHUP to keepalived to reload the configuration.
func (k *keepalived) Reload() error {
	glog.Info("Waiting for keepalived to start")
	for !k.IsRunning() {
		time.Sleep(time.Second)
	}

	k.Cleanup()
	glog.Info("reloading keepalived")
	err := syscall.Kill(k.cmd.Process.Pid, syscall.SIGHUP)
	if err != nil {
		return fmt.Errorf("error reloading keepalived: %v", err)
	}

	return nil
}

// Whether keepalived process is currently running
func (k *keepalived) IsRunning() bool {
	if !k.started {
		glog.Error("keepalived not started")
		return false
	}

	if _, err := os.Stat(keepalivedPid); os.IsNotExist(err) {
		glog.Error("Missing keepalived.pid")
		return false
	}

	return true
}

// Whether keepalived child process is currently running and VIPs are assigned
func (k *keepalived) Healthy() error {
	if !k.IsRunning() {
		return fmt.Errorf("keepalived is not running")
	}

	if _, err := os.Stat(vrrpPid); os.IsNotExist(err) {
		return fmt.Errorf("VRRP child process not running")
	}

	b, err := ioutil.ReadFile(keepalivedState)
	if err != nil {
		return err
	}

	master := false
	state := strings.TrimSpace(string(b))
	if strings.Contains(state, "MASTER") {
		master = true
	}

	var out bytes.Buffer
	cmd := exec.Command("ip", "-brief", "address", "show", k.iface, "up")
	cmd.Stderr = os.Stderr
	cmd.Stdout = &out
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true,
		Pgid:    0,
	}

	err = cmd.Run()
	if err != nil {
		return err
	}

	ips := out.String()
	glog.V(3).Infof("Status of %s interface: %s", state, ips)

	for _, vip := range k.vips {
		containsVip := strings.Contains(ips, fmt.Sprintf(" %s/32 ", vip))

		if master && !containsVip {
			return fmt.Errorf("Missing VIP %s on %s", vip, state)
		} else if !master && containsVip {
			return fmt.Errorf("%s should not contain VIP %s", state, vip)
		}
	}

	// All checks successful
        return nil
}

func (k *keepalived) Cleanup() {
	glog.Infof("Cleanup: %s", k.vips)
	for _, vip := range k.vips {
		k.removeVIP(vip)
	}

	err := k.ipt.FlushChain(iptables.TableFilter, iptables.Chain(iptablesChain))
	if err != nil {
		glog.V(2).Infof("unexpected error flushing iptables chain %v: %v", err, iptablesChain)
	}
}

// Stop stop keepalived process
func (k *keepalived) Stop() {
	k.Cleanup()

	err := syscall.Kill(k.cmd.Process.Pid, syscall.SIGTERM)
	if err != nil {
		glog.Errorf("error stopping keepalived: %v", err)
	}
}

func (k *keepalived) removeVIP(vip string) {
	glog.Infof("removing configured VIP %v", vip)
	out, err := k8sexec.New().Command("ip", "addr", "del", vip+"/32", "dev", k.iface).CombinedOutput()
	if err != nil {
		glog.V(2).Infof("Error removing VIP %s: %v\n%s", vip, err, out)
	}
}

func (k *keepalived) loadTemplates() error {
	tmpl, err := template.ParseFiles(keepalivedTmpl)
	if err != nil {
		return err
	}
	k.keepalivedTmpl = tmpl

	tmpl, err = template.ParseFiles(haproxyTmpl)
	if err != nil {
		return err
	}
	k.haproxyTmpl = tmpl

	return nil
}
<|endoftext|>


<commit_before>package correlation

import (
	"math"
	"testing"

	"github.com/ready-steady/simulation/system"
	"github.com/ready-steady/statistics/decomposition"
	"github.com/ready-steady/support/assert"
)

func TestCorrelateSmall(t *testing.T) {
	_, application, _ := system.Load("fixtures/002_020.tgff")

	C := Compute(application, index(20), 2)
	_, _, err := decomposition.CovPCA(C, 20, 0)
	assert.Success(err, t)

	C = Compute(application, index(1), 2)
	assert.Equal(C, []float64{1}, t)
}

func TestCorrelateLarge(t *testing.T) {
	_, application, _ := system.Load("fixtures/016_160.tgff")

	C := Compute(application, index(160), 5)
	_, _, err := decomposition.CovPCA(C, 160, math.Sqrt(math.Nextafter(1, 2)-1))
	assert.Success(err, t)
}

func TestMeasure(t *testing.T) {
	_, application, _ := system.Load("fixtures/002_020.tgff")
	distance := measure(application)

	cases := []struct {
		i uint
		j uint
		d float64
	}{
		{0, 1, 1},
		{0, 7, 3},
		{0, 18, math.Sqrt(5*5 + 0.5*0.5)},
		{1, 2, math.Sqrt(1*1 + 1*1)},
		{1, 3, 1},
		{2, 3, 1},
		{3, 9, math.Sqrt(1*1 + 2*2)},
		{8, 9, 1},
	}

	for _, c := range cases {
		assert.Equal(distance[20*c.i+c.j], c.d, t)
	}
}

func TestExplore(t *testing.T) {
	_, application, _ := system.Load("fixtures/002_020.tgff")
	depth := explore(application)

	assert.Equal(depth, []uint{
		0,
		1,
		2, 2, 2,
		3, 3, 3, 3, 3,
		4, 4, 4, 4, 4, 4, 4, 4,
		5, 5,
	}, t)
}

func BenchmarkCorrelate(b *testing.B) {
	_, application, _ := system.Load("fixtures/002_020.tgff")
	index := index(20)

	for i := 0; i < b.N; i++ {
		Compute(application, index, 2)
	}
}

func index(count uint) []uint {
	index := make([]uint, count)

	for i := uint(0); i < count; i++ {
		index[i] = i
	}

	return index
}
<commit_msg>Fix the location of assert<commit_after>package correlation

import (
	"math"
	"testing"

	"github.com/ready-steady/assert"
	"github.com/ready-steady/simulation/system"
	"github.com/ready-steady/statistics/decomposition"
)

func TestCorrelateSmall(t *testing.T) {
	_, application, _ := system.Load("fixtures/002_020.tgff")

	C := Compute(application, index(20), 2)
	_, _, err := decomposition.CovPCA(C, 20, 0)
	assert.Success(err, t)

	C = Compute(application, index(1), 2)
	assert.Equal(C, []float64{1}, t)
}

func TestCorrelateLarge(t *testing.T) {
	_, application, _ := system.Load("fixtures/016_160.tgff")

	C := Compute(application, index(160), 5)
	_, _, err := decomposition.CovPCA(C, 160, math.Sqrt(math.Nextafter(1, 2)-1))
	assert.Success(err, t)
}

func TestMeasure(t *testing.T) {
	_, application, _ := system.Load("fixtures/002_020.tgff")
	distance := measure(application)

	cases := []struct {
		i uint
		j uint
		d float64
	}{
		{0, 1, 1},
		{0, 7, 3},
		{0, 18, math.Sqrt(5*5 + 0.5*0.5)},
		{1, 2, math.Sqrt(1*1 + 1*1)},
		{1, 3, 1},
		{2, 3, 1},
		{3, 9, math.Sqrt(1*1 + 2*2)},
		{8, 9, 1},
	}

	for _, c := range cases {
		assert.Equal(distance[20*c.i+c.j], c.d, t)
	}
}

func TestExplore(t *testing.T) {
	_, application, _ := system.Load("fixtures/002_020.tgff")
	depth := explore(application)

	assert.Equal(depth, []uint{
		0,
		1,
		2, 2, 2,
		3, 3, 3, 3, 3,
		4, 4, 4, 4, 4, 4, 4, 4,
		5, 5,
	}, t)
}

func BenchmarkCorrelate(b *testing.B) {
	_, application, _ := system.Load("fixtures/002_020.tgff")
	index := index(20)

	for i := 0; i < b.N; i++ {
		Compute(application, index, 2)
	}
}

func index(count uint) []uint {
	index := make([]uint, count)

	for i := uint(0); i < count; i++ {
		index[i] = i
	}

	return index
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package dispatcher

import (
	"fmt"
	"os"
	"syscall"

	"github.com/GoogleCloudPlatform/kubectl-dispatcher/pkg/client"
	"github.com/GoogleCloudPlatform/kubectl-dispatcher/pkg/filepath"
	"github.com/GoogleCloudPlatform/kubectl-dispatcher/pkg/util"
	"github.com/spf13/pflag"
	"k8s.io/apimachinery/pkg/version"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	utilflag "k8s.io/component-base/cli/flag"

	// klog calls in this file assume it has been initialized beforehand
	"k8s.io/klog"
)

const (
	requestTimeout = "5s"        // Timeout for server version query
	cacheMaxAge    = 2 * 60 * 60 // 2 hours in seconds
)

var HelpFlags = []string{"-h", "--help"}

type Dispatcher struct {
	args            []string
	env             []string
	clientVersion   version.Info
	filepathBuilder *filepath.FilepathBuilder
}

// NewDispatcher returns a new pointer to a Dispatcher struct.
func NewDispatcher(args []string, env []string,
	clientVersion version.Info,
	filepathBuilder *filepath.FilepathBuilder) *Dispatcher {

	return &Dispatcher{
		args:            args,
		env:             env,
		clientVersion:   clientVersion,
		filepathBuilder: filepathBuilder,
	}
}

// GetArgs returns a copy of the slice of strings representing the command line arguments.
func (d *Dispatcher) GetArgs() []string {
	return util.CopyStrSlice(d.args)
}

// GetEnv returns a copy of the slice of environment variables.
func (d *Dispatcher) GetEnv() []string {
	return util.CopyStrSlice(d.env)
}

func (d *Dispatcher) GetClientVersion() version.Info {
	return d.clientVersion
}

const kubeConfigFlagSetName = "dispatcher-kube-config"

// InitKubeConfigFlags returns the ConfigFlags struct filled in with
// kube config values parsed from command line arguments. These flag values can
// affect the server version query. Therefore, the set of kubeConfigFlags MUST
// match the set used in the regular kubectl binary.
func (d *Dispatcher) InitKubeConfigFlags() (*genericclioptions.ConfigFlags, error) {

	// IMPORTANT: If there is an error parsing flags--continue.
	kubeConfigFlagSet := pflag.NewFlagSet("dispatcher-kube-config", pflag.ContinueOnError)
	kubeConfigFlagSet.ParseErrorsWhitelist.UnknownFlags = true
	kubeConfigFlagSet.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)

	unusedParameter := true // Could be either true or false
	kubeConfigFlags := genericclioptions.NewConfigFlags(unusedParameter)
	kubeConfigFlags.AddFlags(kubeConfigFlagSet)

	// Remove help flags, since these are special-cased in pflag.Parse,
	// and handled in the dispatcher instead of passed to versioned binary.
	args := util.FilterList(d.GetArgs(), HelpFlags)
	if err := kubeConfigFlagSet.Parse(args[1:]); err != nil {
		return nil, err
	}
	kubeConfigFlagSet.VisitAll(func(flag *pflag.Flag) {
		klog.V(4).Infof("KubeConfig Flag: --%s=%q", flag.Name, flag.Value)
	})

	return kubeConfigFlags, nil
}

// Dispatch attempts to execute a matching version of kubectl based on the
// version of the APIServer. If successful, this method will not return, since
// current process will be overwritten (see execve(2)). Otherwise, this method
// returns an error describing why the execution could not happen.
func (d *Dispatcher) Dispatch() error {
	// Fetch the server version and generate the kubectl binary full file path
	// from this version.
	// Example:
	//   serverVersion=1.11 -> /home/seans/go/bin/kubectl.1.11
	kubeConfigFlags, err := d.InitKubeConfigFlags()
	if err != nil {
		return err
	}
	svclient := client.NewServerVersionClient(kubeConfigFlags)
	svclient.SetRequestTimeout(requestTimeout)
	svclient.SetCacheMaxAge(cacheMaxAge)
	serverVersion, err := svclient.ServerVersion()
	if err != nil {
		return err
	}
	klog.V(4).Infof("Server Version: %s", serverVersion.GitVersion)
	klog.V(4).Infof("Client Version: %s", d.GetClientVersion().GitVersion)
	if util.VersionMatch(d.GetClientVersion(), *serverVersion) {
		// TODO(seans): Consider changing to return a bool as well as error, since
		// this isn't really an error.
		return fmt.Errorf("Client/Server version match--fall through to default")
	}

	kubectlFilepath, err := d.filepathBuilder.VersionedFilePath(*serverVersion)
	if err != nil {
		return err
	}
	if err := d.filepathBuilder.ValidateFilepath(kubectlFilepath); err != nil {
		return err
	}

	// Delegate to the versioned kubectl binary. This overwrites the current process
	// (by calling execve(2) system call), and it does not return on success.
	klog.V(3).Infof("kubectl dispatching: %s\n", kubectlFilepath)
	return syscall.Exec(kubectlFilepath, d.GetArgs(), d.GetEnv())
}

// Execute is the entry point to the dispatcher. It passes in the current client
// version, which is used to determine if a delegation is necessary. If this function
// successfully delegates, then it will NOT return, since the current process will be
// overwritten (see execve(2)). If this function does not delegate, it merely falls
// through. This function assumes logging has been initialized before it is run;
// otherwise, log statements will not work.
func Execute(clientVersion version.Info) {
	klog.Info("Starting dispatcher")
	filepathBuilder := filepath.NewFilepathBuilder(&filepath.ExeDirGetter{}, os.Stat)
	dispatcher := NewDispatcher(os.Args, os.Environ(), clientVersion, filepathBuilder)
	if err := dispatcher.Dispatch(); err != nil {
		klog.Warningf("Dispatch error: %v", err)
	}
}
<commit_msg>Updated klog statements to add level<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package dispatcher

import (
	"fmt"
	"os"
	"syscall"

	"github.com/GoogleCloudPlatform/kubectl-dispatcher/pkg/client"
	"github.com/GoogleCloudPlatform/kubectl-dispatcher/pkg/filepath"
	"github.com/GoogleCloudPlatform/kubectl-dispatcher/pkg/util"
	"github.com/spf13/pflag"
	"k8s.io/apimachinery/pkg/version"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	utilflag "k8s.io/component-base/cli/flag"

	// klog calls in this file assume it has been initialized beforehand
	"k8s.io/klog"
)

const (
	requestTimeout = "5s"        // Timeout for server version query
	cacheMaxAge    = 2 * 60 * 60 // 2 hours in seconds
)

var HelpFlags = []string{"-h", "--help"}

type Dispatcher struct {
	args            []string
	env             []string
	clientVersion   version.Info
	filepathBuilder *filepath.FilepathBuilder
}

// NewDispatcher returns a new pointer to a Dispatcher struct.
func NewDispatcher(args []string, env []string,
	clientVersion version.Info,
	filepathBuilder *filepath.FilepathBuilder) *Dispatcher {

	return &Dispatcher{
		args:            args,
		env:             env,
		clientVersion:   clientVersion,
		filepathBuilder: filepathBuilder,
	}
}

// GetArgs returns a copy of the slice of strings representing the command line arguments.
func (d *Dispatcher) GetArgs() []string {
	return util.CopyStrSlice(d.args)
}

// GetEnv returns a copy of the slice of environment variables.
func (d *Dispatcher) GetEnv() []string {
	return util.CopyStrSlice(d.env)
}

func (d *Dispatcher) GetClientVersion() version.Info {
	return d.clientVersion
}

const kubeConfigFlagSetName = "dispatcher-kube-config"

// InitKubeConfigFlags returns the ConfigFlags struct filled in with
// kube config values parsed from command line arguments. These flag values can
// affect the server version query. Therefore, the set of kubeConfigFlags MUST
// match the set used in the regular kubectl binary.
func (d *Dispatcher) InitKubeConfigFlags() (*genericclioptions.ConfigFlags, error) {

	// IMPORTANT: If there is an error parsing flags--continue.
	kubeConfigFlagSet := pflag.NewFlagSet("dispatcher-kube-config", pflag.ContinueOnError)
	kubeConfigFlagSet.ParseErrorsWhitelist.UnknownFlags = true
	kubeConfigFlagSet.SetNormalizeFunc(utilflag.WordSepNormalizeFunc)

	unusedParameter := true // Could be either true or false
	kubeConfigFlags := genericclioptions.NewConfigFlags(unusedParameter)
	kubeConfigFlags.AddFlags(kubeConfigFlagSet)

	// Remove help flags, since these are special-cased in pflag.Parse,
	// and handled in the dispatcher instead of passed to versioned binary.
	args := util.FilterList(d.GetArgs(), HelpFlags)
	if err := kubeConfigFlagSet.Parse(args[1:]); err != nil {
		return nil, err
	}
	kubeConfigFlagSet.VisitAll(func(flag *pflag.Flag) {
		klog.V(4).Infof("KubeConfig Flag: --%s=%q", flag.Name, flag.Value)
	})

	return kubeConfigFlags, nil
}

// Dispatch attempts to execute a matching version of kubectl based on the
// version of the APIServer. If successful, this method will not return, since
// current process will be overwritten (see execve(2)). Otherwise, this method
// returns an error describing why the execution could not happen.
func (d *Dispatcher) Dispatch() error {
	// Fetch the server version and generate the kubectl binary full file path
	// from this version.
	// Example:
	//   serverVersion=1.11 -> /home/seans/go/bin/kubectl.1.11
	kubeConfigFlags, err := d.InitKubeConfigFlags()
	if err != nil {
		return err
	}
	svclient := client.NewServerVersionClient(kubeConfigFlags)
	svclient.SetRequestTimeout(requestTimeout)
	svclient.SetCacheMaxAge(cacheMaxAge)
	serverVersion, err := svclient.ServerVersion()
	if err != nil {
		return err
	}
	klog.V(4).Infof("Server Version: %s", serverVersion.GitVersion)
	klog.V(4).Infof("Client Version: %s", d.GetClientVersion().GitVersion)
	if util.VersionMatch(d.GetClientVersion(), *serverVersion) {
		// TODO(seans): Consider changing to return a bool as well as error, since
		// this isn't really an error.
		return fmt.Errorf("Client/Server version match--fall through to default")
	}

	kubectlFilepath, err := d.filepathBuilder.VersionedFilePath(*serverVersion)
	if err != nil {
		return err
	}
	if err := d.filepathBuilder.ValidateFilepath(kubectlFilepath); err != nil {
		return err
	}

	// Delegate to the versioned kubectl binary. This overwrites the current process
	// (by calling execve(2) system call), and it does not return on success.
	klog.V(3).Infof("kubectl dispatching: %s\n", kubectlFilepath)
	return syscall.Exec(kubectlFilepath, d.GetArgs(), d.GetEnv())
}

// Execute is the entry point to the dispatcher. It passes in the current client
// version, which is used to determine if a delegation is necessary. If this function
// successfully delegates, then it will NOT return, since the current process will be
// overwritten (see execve(2)). If this function does not delegate, it merely falls
// through. This function assumes logging has been initialized before it is run;
// otherwise, log statements will not work.
func Execute(clientVersion version.Info) {
	klog.V(4).Info("Starting dispatcher")
	filepathBuilder := filepath.NewFilepathBuilder(&filepath.ExeDirGetter{}, os.Stat)
	dispatcher := NewDispatcher(os.Args, os.Environ(), clientVersion, filepathBuilder)
	if err := dispatcher.Dispatch(); err != nil {
		klog.V(3).Infof("Dispatch error: %v", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// MerkleAudit runs a merkle tree audit in the background once in a while.
// It verifies the skips of a randomly chosen merkle tree root, making
// sure that the server is not tampering with the merkle trees.

package engine

import (
	"crypto/rand"
	"errors"
	"fmt"
	"math/big"
	"sync"
	"time"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
)

var (
	errAuditOffline    = errors.New("Merkle audit failed to run due to the lack of connectivity.")
	errAuditNoLastRoot = errors.New("Merkle audit failed to run due to not being able to get the last root.")
)

var MerkleAuditSettings = BackgroundTaskSettings{
	Start:        5 * time.Minute,
	StartStagger: 1 * time.Hour,
	Interval:     6 * time.Hour,
	Limit:        1 * time.Minute,
}

// MerkleAudit is an engine.
type MerkleAudit struct {
	libkb.Contextified
	sync.Mutex

	args *MerkleAuditArgs
	task *BackgroundTask
}

type MerkleAuditArgs struct {
	// Channels used for testing. Normally nil.
	testingMetaCh     chan<- string
	testingRoundResCh chan<- error
}

type merkleAuditState struct {
	RetrySeqno *keybase1.Seqno `json:"retrySeqno"`
	LastSeqno  *keybase1.Seqno `json:"lastSeqno"`
}

// NewMerkleAudit creates a new MerkleAudit engine.
func NewMerkleAudit(g *libkb.GlobalContext, args *MerkleAuditArgs) *MerkleAudit {
	task := NewBackgroundTask(g, &BackgroundTaskArgs{
		Name:     "MerkleAudit",
		F:        MerkleAuditRound,
		Settings: MerkleAuditSettings,

		testingMetaCh:     args.testingMetaCh,
		testingRoundResCh: args.testingRoundResCh,
	})
	return &MerkleAudit{
		Contextified: libkb.NewContextified(g),
		args:         args,
		task:         task,
	}
}

// Name is the unique engine name.
func (e *MerkleAudit) Name() string {
	return "MerkleAudit"
}

// GetPrereqs returns the engine prereqs.
func (e *MerkleAudit) Prereqs() Prereqs {
	return Prereqs{}
}

// RequiredUIs returns the required UIs.
func (e *MerkleAudit) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{}
}

// SubConsumers returns the other UI consumers for this engine.
func (e *MerkleAudit) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{}
}

// Run starts the engine.
// Returns immediately, kicks off a background goroutine.
func (e *MerkleAudit) Run(m libkb.MetaContext) (err error) {
	if m.G().GetEnv().GetDisableMerkleAuditor() {
		m.G().Log.CDebugf(m.Ctx(), "merkle audit disabled, aborting run")
	}
	return RunEngine2(m, e.task)
}

func (e *MerkleAudit) Shutdown() {
	e.task.Shutdown()
}

// randSeqno picks a random number between [low, high) that's different from prev.
func randSeqno(lo keybase1.Seqno, hi keybase1.Seqno, prev *keybase1.Seqno) (keybase1.Seqno, error) {
	// Prevent an infinite loop if [0,1) and prev = 0
	if hi-lo == 1 && prev != nil && *prev == lo {
		return keybase1.Seqno(0), fmt.Errorf("unable to generate a non-duplicate seqno other than %d", *prev)
	}
	for {
		rangeBig := big.NewInt(int64(hi - lo))
		n, err := rand.Int(rand.Reader, rangeBig)
		if err != nil {
			return keybase1.Seqno(0), err
		}
		newSeqno := keybase1.Seqno(n.Int64()) + lo
		if prev == nil || *prev != newSeqno {
			return newSeqno, nil
		}
	}
}

var merkleAuditKey = libkb.DbKey{
	Typ: libkb.DBMerkleAudit,
	Key: "root",
}

func lookupMerkleAuditRetryFromState(m libkb.MetaContext) (*keybase1.Seqno, *keybase1.Seqno, error) {
	var state merkleAuditState
	found, err := m.G().LocalDb.GetInto(&state, merkleAuditKey)
	if err != nil {
		return nil, nil, err
	}
	if !found {
		// Nothing found, no error
		return nil, nil, nil
	}

	// Can still be nil
	return state.RetrySeqno, state.LastSeqno, nil
}

func saveMerkleAuditState(m libkb.MetaContext, state merkleAuditState) error {
	return m.G().LocalDb.PutObj(merkleAuditKey, nil, state)
}

func performMerkleAudit(m libkb.MetaContext, startSeqno keybase1.Seqno) error {
	if m.G().ConnectivityMonitor.IsConnected(m.Ctx()) == libkb.ConnectivityMonitorNo {
		m.Debug("MerkleAudit giving up offline")
		return errAuditOffline
	}

	// Acquire the most recent merkle tree root
	lastRoot := m.G().MerkleClient.LastRoot(m)
	if lastRoot == nil {
		m.Debug("MerkleAudit unable to retrieve the last root")
		return errAuditNoLastRoot
	}

	// We can copy the pointer's value as it can only return nil if root == nil.
	lastSeqno := *lastRoot.Seqno()

	// Acquire the first root and calculate its hash
	startRoot, err := m.G().MerkleClient.LookupRootAtSeqno(m, startSeqno)
	if err != nil {
		return err
	}
	startHash := startRoot.ShortHash()

	// Traverse the merkle tree seqnos
	currentSeqno := startSeqno + 1
	step := 1
	for {
		// Proceed until the last known root
		if currentSeqno > lastSeqno {
			break
		}

		currentRoot, err := m.G().MerkleClient.LookupRootAtSeqno(m, currentSeqno)
		if err != nil {
			return err
		}
		currentHash := currentRoot.SkipToSeqno(startSeqno)
		if currentHash == nil {
			return libkb.NewClientMerkleSkipMissingError(
				fmt.Sprintf("Root %d missing skip hash to %d", currentSeqno, startSeqno),
			)
		}

		if !startHash.Eq(currentHash) {
			// Warn the user about the possibility of the server tampering with the roots.
			return libkb.NewClientMerkleSkipHashMismatchError(
				fmt.Sprintf("Invalid skip hash from %d to %d", currentSeqno, startSeqno),
			)
		}

		// We're doing this exponentially to make use of the skips.
		currentSeqno += keybase1.Seqno(step)
		step *= 2
	}

	return nil
}

func MerkleAuditRound(m libkb.MetaContext) (err error) {
	m = m.WithLogTag("MAUDT")
	defer m.TraceTimed("MerkleAuditRound", func() error { return err })()

	// Look up any previously requested retries
	startSeqno, prevSeqno, err := lookupMerkleAuditRetryFromState(m)
	if err != nil {
		m.Debug("MerkleAudit unable to acquire saved state from localdb")
		return nil
	}

	// If no retry was requested
	if startSeqno == nil {
		// nil seqno, generate a new one:
		// 1. Acquire the most recent merkle tree root
		lastRoot := m.G().MerkleClient.LastRoot(m)
		if lastRoot == nil {
			m.Debug("MerkleAudit unable to retrieve the last root")
			return nil
		}
		lastSeqno := *lastRoot.Seqno()

		// 2. Figure out the first merkle root seqno with skips, fall back to 1
		firstSeqno := m.G().MerkleClient.FirstExaminableHistoricalRoot(m)
		if firstSeqno == nil {
			val := keybase1.Seqno(1)
			firstSeqno = &val
		}

		// 3. Generate a random seqno for the starting root in the audit.
		randomSeqno, err := randSeqno(*firstSeqno, lastSeqno, prevSeqno)
		if err != nil {
			return err
		}
		startSeqno = &randomSeqno
	}

	// If this time it fails, save it
	err = performMerkleAudit(m, *startSeqno)
	if err == nil {
		// Early return for fewer ifs
		return saveMerkleAuditState(m, merkleAuditState{
			RetrySeqno: nil,
			LastSeqno:  startSeqno,
		})
	}

	// All MerkleClientErrors would suggest that the server is tampering with the roots
	if _, ok := err.(libkb.MerkleClientError); ok {
		m.Error("MerkleAudit fatally failed: %s", err)
		// Send the notification to the client
		m.G().NotifyRouter.HandleRootAuditError(fmt.Sprintf(
			"Merkle tree audit from %d failed: %s",
			startSeqno, err.Error(),
		))
	} else {
		m.Debug("MerkleAudit could not complete: %s", err)
	}

	// Use another error variable to prevent shadowing
	if serr := saveMerkleAuditState(m, merkleAuditState{
		RetrySeqno: startSeqno,
		LastSeqno:  prevSeqno,
	}); serr != nil {
		return serr
	}

	return err
}
<commit_msg>engine: fix merkle audit retries before checkpoint (#17484)<commit_after>// Copyright 2019 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// MerkleAudit runs a merkle tree audit in the background once in a while.
// It verifies the skips of a randomly chosen merkle tree root, making
// sure that the server is not tampering with the merkle trees.

package engine

import (
	"crypto/rand"
	"errors"
	"fmt"
	"math/big"
	"sync"
	"time"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
)

var (
	errAuditOffline    = errors.New("Merkle audit failed to run due to the lack of connectivity.")
	errAuditNoLastRoot = errors.New("Merkle audit failed to run due to not being able to get the last root.")
)

var MerkleAuditSettings = BackgroundTaskSettings{
	Start:        5 * time.Minute,
	StartStagger: 1 * time.Hour,
	Interval:     6 * time.Hour,
	Limit:        1 * time.Minute,
}

// MerkleAudit is an engine.
type MerkleAudit struct {
	libkb.Contextified
	sync.Mutex

	args *MerkleAuditArgs
	task *BackgroundTask
}

type MerkleAuditArgs struct {
	// Channels used for testing. Normally nil.
	testingMetaCh     chan<- string
	testingRoundResCh chan<- error
}

// Bump this up whenever there is a change that needs to reset the current stored state.
const merkleAuditCurrentVersion = 1

type merkleAuditState struct {
	RetrySeqno *keybase1.Seqno `json:"retrySeqno"`
	LastSeqno  *keybase1.Seqno `json:"lastSeqno"`
	Version    int             `json:"version"`
}

// NewMerkleAudit creates a new MerkleAudit engine.
func NewMerkleAudit(g *libkb.GlobalContext, args *MerkleAuditArgs) *MerkleAudit {
	task := NewBackgroundTask(g, &BackgroundTaskArgs{
		Name:     "MerkleAudit",
		F:        MerkleAuditRound,
		Settings: MerkleAuditSettings,

		testingMetaCh:     args.testingMetaCh,
		testingRoundResCh: args.testingRoundResCh,
	})
	return &MerkleAudit{
		Contextified: libkb.NewContextified(g),
		args:         args,
		task:         task,
	}
}

// Name is the unique engine name.
func (e *MerkleAudit) Name() string {
	return "MerkleAudit"
}

// GetPrereqs returns the engine prereqs.
func (e *MerkleAudit) Prereqs() Prereqs {
	return Prereqs{}
}

// RequiredUIs returns the required UIs.
func (e *MerkleAudit) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{}
}

// SubConsumers returns the other UI consumers for this engine.
func (e *MerkleAudit) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{}
}

// Run starts the engine.
// Returns immediately, kicks off a background goroutine.
func (e *MerkleAudit) Run(m libkb.MetaContext) (err error) {
	if m.G().GetEnv().GetDisableMerkleAuditor() {
		m.G().Log.CDebugf(m.Ctx(), "merkle audit disabled, aborting run")
	}
	return RunEngine2(m, e.task)
}

func (e *MerkleAudit) Shutdown() {
	e.task.Shutdown()
}

// randSeqno picks a random number between [low, high) that's different from prev.
func randSeqno(lo keybase1.Seqno, hi keybase1.Seqno, prev *keybase1.Seqno) (keybase1.Seqno, error) {
	// Prevent an infinite loop if [0,1) and prev = 0
	if hi-lo == 1 && prev != nil && *prev == lo {
		return keybase1.Seqno(0), fmt.Errorf("unable to generate a non-duplicate seqno other than %d", *prev)
	}
	for {
		rangeBig := big.NewInt(int64(hi - lo))
		n, err := rand.Int(rand.Reader, rangeBig)
		if err != nil {
			return keybase1.Seqno(0), err
		}
		newSeqno := keybase1.Seqno(n.Int64()) + lo
		if prev == nil || *prev != newSeqno {
			return newSeqno, nil
		}
	}
}

var merkleAuditKey = libkb.DbKey{
	Typ: libkb.DBMerkleAudit,
	Key: "root",
}

func lookupMerkleAuditRetryFromState(m libkb.MetaContext) (*keybase1.Seqno, *keybase1.Seqno, error) {
	var state merkleAuditState
	found, err := m.G().LocalDb.GetInto(&state, merkleAuditKey)
	if err != nil {
		return nil, nil, err
	}
	if !found {
		// Nothing found, no error
		return nil, nil, nil
	}
	if state.Version != merkleAuditCurrentVersion {
		m.Debug("discarding state with version %d, which isn't %d", state.Version, merkleAuditCurrentVersion)
		return nil, nil, nil
	}

	// Can still be nil
	return state.RetrySeqno, state.LastSeqno, nil
}

func saveMerkleAuditState(m libkb.MetaContext, state merkleAuditState) error {
	state.Version = merkleAuditCurrentVersion
	return m.G().LocalDb.PutObj(merkleAuditKey, nil, state)
}

func performMerkleAudit(m libkb.MetaContext, startSeqno keybase1.Seqno) error {
	if m.G().ConnectivityMonitor.IsConnected(m.Ctx()) == libkb.ConnectivityMonitorNo {
		m.Debug("MerkleAudit giving up offline")
		return errAuditOffline
	}

	// Acquire the most recent merkle tree root
	lastRoot := m.G().MerkleClient.LastRoot(m)
	if lastRoot == nil {
		m.Debug("MerkleAudit unable to retrieve the last root")
		return errAuditNoLastRoot
	}

	// We can copy the pointer's value as it can only return nil if root == nil.
	lastSeqno := *lastRoot.Seqno()

	// Acquire the first root and calculate its hash
	startRoot, err := m.G().MerkleClient.LookupRootAtSeqno(m, startSeqno)
	if err != nil {
		return err
	}
	startHash := startRoot.ShortHash()

	// Traverse the merkle tree seqnos
	currentSeqno := startSeqno + 1
	step := 1
	for {
		// Proceed until the last known root
		if currentSeqno > lastSeqno {
			break
		}

		currentRoot, err := m.G().MerkleClient.LookupRootAtSeqno(m, currentSeqno)
		if err != nil {
			return err
		}
		currentHash := currentRoot.SkipToSeqno(startSeqno)
		if currentHash == nil {
			return libkb.NewClientMerkleSkipMissingError(
				fmt.Sprintf("Root %d missing skip hash to %d", currentSeqno, startSeqno),
			)
		}

		if !startHash.Eq(currentHash) {
			// Warn the user about the possibility of the server tampering with the roots.
			return libkb.NewClientMerkleSkipHashMismatchError(
				fmt.Sprintf("Invalid skip hash from %d to %d", currentSeqno, startSeqno),
			)
		}

		// We're doing this exponentially to make use of the skips.
		currentSeqno += keybase1.Seqno(step)
		step *= 2
	}

	return nil
}

func MerkleAuditRound(m libkb.MetaContext) (err error) {
	m = m.WithLogTag("MAUDT")
	defer m.TraceTimed("MerkleAuditRound", func() error { return err })()

	// Look up any previously requested retries
	startSeqno, prevSeqno, err := lookupMerkleAuditRetryFromState(m)
	if err != nil {
		m.Debug("MerkleAudit unable to acquire saved state from localdb")
		return nil
	}

	// If no retry was requested
	if startSeqno == nil {
		// nil seqno, generate a new one:
		// 1. Acquire the most recent merkle tree root
		lastRoot := m.G().MerkleClient.LastRoot(m)
		if lastRoot == nil {
			m.Debug("MerkleAudit unable to retrieve the last root")
			return nil
		}
		lastSeqno := *lastRoot.Seqno()

		// 2. Figure out the first merkle root seqno with skips, fall back to 1
		firstSeqno := m.G().MerkleClient.FirstExaminableHistoricalRoot(m)
		if firstSeqno == nil {
			val := keybase1.Seqno(1)
			firstSeqno = &val
		}

		// 3. Generate a random seqno for the starting root in the audit.
		randomSeqno, err := randSeqno(*firstSeqno, lastSeqno, prevSeqno)
		if err != nil {
			return err
		}
		startSeqno = &randomSeqno
	} else {
		m.Debug("Audit retry requested for %d", *startSeqno)
	}

	// If this time it fails, save it
	err = performMerkleAudit(m, *startSeqno)
	if err == nil {
		// Early return for fewer ifs
		return saveMerkleAuditState(m, merkleAuditState{
			RetrySeqno: nil,
			LastSeqno:  startSeqno,
		})
	}

	// All MerkleClientErrors would suggest that the server is tampering with the roots
	if _, ok := err.(libkb.MerkleClientError); ok {
		m.Error("MerkleAudit fatally failed: %s", err)
		// Send the notification to the client
		m.G().NotifyRouter.HandleRootAuditError(fmt.Sprintf(
			"Merkle tree audit from %d failed: %s",
			startSeqno, err.Error(),
		))
	} else {
		m.Debug("MerkleAudit could not complete: %s", err)
	}

	// Use another error variable to prevent shadowing
	if serr := saveMerkleAuditState(m, merkleAuditState{
		RetrySeqno: startSeqno,
		LastSeqno:  prevSeqno,
	}); serr != nil {
		return serr
	}

	return err
}
<|endoftext|>