<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 core

import (
	"errors"
	"github.com/ServiceComb/service-center/pkg/util"
	"github.com/ServiceComb/service-center/pkg/validate"
	pb "github.com/ServiceComb/service-center/server/core/proto"
	"math"
	"reflect"
	"regexp"
)

var (
	ServiceAPI  pb.ServiceCtrlServer
	InstanceAPI pb.SerivceInstanceCtrlServerEx

	VersionRegex                  *regexp.Regexp
	MicroServiceValidator         validate.Validator
	MicroServiceInstanceValidator validate.Validator
	ServiceRuleValidator          validate.Validator
	ServicePathValidator          validate.Validator
	HealthCheckInfoValidator      validate.Validator
	MicroServiceKeyValidator      validate.Validator
	DataCenterInfoValidator       validate.Validator
	GetMSExistsReqValidator       validate.Validator
	GetSchemaExistsReqValidator   validate.Validator
	GetServiceReqValidator        validate.Validator
	GetSchemaReqValidator         validate.Validator
	ConsumerMsValidator           validate.Validator
	ProviderMsValidator           validate.Validator
	TagReqValidator               validate.Validator
	FindInstanceReqValidator      validate.Validator
	GetInstanceValidator          validate.Validator
	SchemasValidator              validate.Validator
	SchemaValidator               validate.Validator
	FrameWKValidator              validate.Validator

	SchemaIdRule *validate.ValidateRule
	TagRule      *validate.ValidateRule
)

func init() {
	// 非map/slice的validator
	nameRegex, _ := regexp.Compile(`^[a-zA-Z0-9]*$|^[a-zA-Z0-9][a-zA-Z0-9_\-.]*[a-zA-Z0-9]$`)
	serviceNameForFindRegex, _ := regexp.Compile(`^[a-zA-Z0-9]*$|^[a-zA-Z0-9][a-zA-Z0-9_\-.:]*[a-zA-Z0-9]$`)
	//name模糊规则: name, *
	nameFuzzyRegex, _ := regexp.Compile(`^[a-zA-Z0-9]*$|^[a-zA-Z0-9][a-zA-Z0-9_\-.]*[a-zA-Z0-9]$|^\*$`)
	VersionRegex, _ = regexp.Compile(`^[0-9]+(\.[0-9]+){0,2}$`)
	// version模糊规则: 1.0, 1.0+, 1.0-2.0, latest
	versionFuzzyRegex, _ := regexp.Compile(`^[0-9]*$|^[0-9]+(\.[0-9]+)*\+{0,1}$|^[0-9]+(\.[0-9]+)*-[0-9]+(\.[0-9]+)*$|^latest$`)
	pathRegex, _ := regexp.Compile(`^[A-Za-z0-9.,?'\\/+&%$#=~_\-@{}]*$`)
	descriptionRegex, _ := regexp.Compile(`^[\p{Han}\w\s。.:*,\-：”“]*$`)
	levelRegex, _ := regexp.Compile(`^(FRONT|MIDDLE|BACK)$`)
	statusRegex, _ := regexp.Compile("^(" + pb.MS_UP + "|" + pb.MS_DOWN + ")*$")
	serviceIdRegex, _ := regexp.Compile(`^.*$`)
	aliasRegex, _ := regexp.Compile(`^[a-zA-Z0-9_\-.:]*$`)
	frameversionRegex, _ := regexp.Compile(`^[a-zA-Z0-9_\-.]*$`)
	registerByRegex, _ := regexp.Compile("^(" + util.StringJoin([]string{
		pb.REGISTERBY_SDK, pb.REGISTERBY_PLATFORM, pb.REGISTERBY_SIDECAR, pb.REGISTERBY_UNKNOWM}, "|") + ")*$")
	envRegex, _ := regexp.Compile("^(" + util.StringJoin([]string{
		pb.ENV_DEV, pb.ENV_TEST, pb.ENV_ACCEPT, pb.ENV_PROD}, "|") + ")*$")
	// map/slice元素的validator
	// 元素的格式和长度由正则控制
	// map/slice的长度由validator中的min/max/length控制
	schemaIdRegex, _ := regexp.Compile(`^[a-zA-Z0-9]{1,160}$|^[a-zA-Z0-9][a-zA-Z0-9_\-.]{0,158}[a-zA-Z0-9]$`) //length:{1,160}
	instStatusRegex, _ := regexp.Compile("^(" + util.StringJoin([]string{
		pb.MSI_UP, pb.MSI_DOWN, pb.MSI_STARTING, pb.MSI_OUTOFSERVICE}, "|") + ")$")
	tagRegex, _ := regexp.Compile(`^[a-zA-Z][a-zA-Z0-9_\-.]{0,63}$`)
	hbModeRegex, _ := regexp.Compile(`^(push|pull)$`)
	numberAllowEmptyRegex, _ := regexp.Compile(`^[0-9]*$`)
	numberRegex, _ := regexp.Compile(`^[0-9]+$`)
	epRegex, _ := regexp.Compile(`^[A-Za-z0-9:/?=&%_.-]+$`)
	simpleNameAllowEmptyRegex, _ := regexp.Compile(`^[A-Za-z0-9_.-]*$`)
	simpleNameRegex, _ := regexp.Compile(`^[A-Za-z0-9_.-]+$`)
	regionRegex, _ := regexp.Compile(`^[A-Za-z0-9_.-]+$`)
	ruleRegex, _ := regexp.Compile(`^(WHITE|BLACK)$`)
	ruleAttrRegex, _ := regexp.Compile(`((^tag_[a-zA-Z][a-zA-Z0-9_\-.]{0,63}$)|(^ServiceId$)|(^AppId$)|(^ServiceName$)|(^Version$)|(^Description$)|(^Level$)|(^Status$))`)
	SchemaSummaryRegex, _ := regexp.Compile(`(a-zA-Z0-9)*`)

	ServiceIdRule := &validate.ValidateRule{Min: 1, Length: 64, Regexp: serviceIdRegex}
	InstanceStatusRule := &validate.ValidateRule{Regexp: instStatusRegex}
	SchemaIdRule = &validate.ValidateRule{Regexp: schemaIdRegex}
	nameRule := &validate.ValidateRule{Min: 1, Max: 128, Regexp: nameRegex}
	versionFuzzyRule := &validate.ValidateRule{Min: 1, Max: 128, Regexp: versionFuzzyRegex}
	TagRule = &validate.ValidateRule{Regexp: tagRegex}

	MicroServiceKeyValidator.AddRule("Environment", &validate.ValidateRule{Regexp: envRegex})
	MicroServiceKeyValidator.AddRule("AppId", &validate.ValidateRule{Min: 1, Max: 160, Regexp: nameRegex})
	MicroServiceKeyValidator.AddRule("ServiceName", nameRule)
	MicroServiceKeyValidator.AddRule("Version", &validate.ValidateRule{Min: 1, Max: 64, Regexp: VersionRegex})

	ServicePathValidator.AddRule("Path", &validate.ValidateRule{Regexp: pathRegex})

	FrameWKValidator.AddRule("Name", &validate.ValidateRule{Min: 1, Max: 64, Regexp: nameRegex})
	FrameWKValidator.AddRule("Version", &validate.ValidateRule{Length: 64, Regexp: frameversionRegex})

	MicroServiceValidator.AddRules(MicroServiceKeyValidator.GetRules())
	MicroServiceValidator.AddRule("Description", &validate.ValidateRule{Length: 256, Regexp: descriptionRegex})
	MicroServiceValidator.AddRule("Level", &validate.ValidateRule{Min: 1, Regexp: levelRegex})
	MicroServiceValidator.AddRule("Status", &validate.ValidateRule{Min: 1, Regexp: statusRegex})
	MicroServiceValidator.AddRule("Schemas", SchemaIdRule)
	MicroServiceValidator.AddSub("Paths", &ServicePathValidator)
	MicroServiceValidator.AddRule("Alias", &validate.ValidateRule{Length: 128, Regexp: aliasRegex})
	MicroServiceValidator.AddRule("RegisterBy", &validate.ValidateRule{Min: 1, Length: 64, Regexp: registerByRegex})
	MicroServiceValidator.AddSub("Framework", &FrameWKValidator)

	GetMSExistsReqValidator.AddRules(MicroServiceKeyValidator.GetRules())
	GetMSExistsReqValidator.AddRule("Version", versionFuzzyRule)

	GetSchemaExistsReqValidator.AddRule("ServiceId", ServiceIdRule)
	GetSchemaExistsReqValidator.AddRule("SchemaId", SchemaIdRule)

	var subSchemaValidor validate.Validator
	subSchemaValidor.AddRule("SchemaId", SchemaIdRule)
	subSchemaValidor.AddRule("Summary", &validate.ValidateRule{Min: 1, Max: 512, Regexp: SchemaSummaryRegex})
	subSchemaValidor.AddRule("Schema", &validate.ValidateRule{Min: 1})

	SchemasValidator.AddRule("ServiceId", ServiceIdRule)
	SchemasValidator.AddSub("Schemas", &subSchemaValidor)

	SchemaValidator.AddRules(subSchemaValidor.GetRules())
	SchemaValidator.AddRule("ServiceId", ServiceIdRule)
	SchemaValidator.AddRule("Summary", &validate.ValidateRule{Max: 512, Regexp: SchemaSummaryRegex})

	GetServiceReqValidator.AddRule("ServiceId", ServiceIdRule)

	GetSchemaReqValidator.AddRule("ServiceId", ServiceIdRule)
	GetSchemaReqValidator.AddRule("SchemaId", SchemaIdRule)

	ConsumerMsValidator.AddRules(MicroServiceKeyValidator.GetRules())

	ProviderMsValidator.AddRules(MicroServiceKeyValidator.GetRules())
	ProviderMsValidator.AddRule("ServiceName", &validate.ValidateRule{Min: 1, Max: 128, Regexp: nameFuzzyRegex})
	ProviderMsValidator.AddRule("Version", versionFuzzyRule)

	TagReqValidator.AddRule("ServiceId", ServiceIdRule)
	TagReqValidator.AddRule("Tags", TagRule)
	TagReqValidator.AddRule("Keys", &validate.ValidateRule{Regexp: tagRegex})
	TagReqValidator.AddRule("Key", &validate.ValidateRule{Regexp: tagRegex})
	TagReqValidator.AddRule("Value", &validate.ValidateRule{Regexp: tagRegex})

	HealthCheckInfoValidator.AddRule("Mode", &validate.ValidateRule{Regexp: hbModeRegex})
	HealthCheckInfoValidator.AddRule("Port", &validate.ValidateRule{Max: math.MaxInt16, Regexp: numberAllowEmptyRegex})
	HealthCheckInfoValidator.AddRule("Times", &validate.ValidateRule{Max: math.MaxInt32, Regexp: numberRegex})
	HealthCheckInfoValidator.AddRule("Interval", &validate.ValidateRule{Max: math.MaxInt32, Regexp: numberRegex})
	HealthCheckInfoValidator.AddRule("Url", &validate.ValidateRule{Regexp: pathRegex})

	MicroServiceInstanceValidator.AddRule("InstanceId", &validate.ValidateRule{Length: 64, Regexp: simpleNameAllowEmptyRegex})
	MicroServiceInstanceValidator.AddRule("ServiceId", ServiceIdRule)
	MicroServiceInstanceValidator.AddRule("Endpoints", &validate.ValidateRule{Regexp: epRegex})
	MicroServiceInstanceValidator.AddRule("HostName", &validate.ValidateRule{Length: 64, Regexp: simpleNameRegex})
	MicroServiceInstanceValidator.AddSub("HealthCheck", &HealthCheckInfoValidator)
	MicroServiceInstanceValidator.AddRule("Status", InstanceStatusRule)
	MicroServiceInstanceValidator.AddSub("DataCenterInfo", &DataCenterInfoValidator)

	DataCenterInfoValidator.AddRule("Name", &validate.ValidateRule{Length: 128, Regexp: simpleNameRegex})
	DataCenterInfoValidator.AddRule("Region", &validate.ValidateRule{Length: 128, Regexp: regionRegex})
	DataCenterInfoValidator.AddRule("AvailableZone", &validate.ValidateRule{Length: 128, Regexp: regionRegex})

	ServiceRuleValidator.AddRule("RuleType", &validate.ValidateRule{Regexp: ruleRegex})
	ServiceRuleValidator.AddRule("Attribute", &validate.ValidateRule{Regexp: ruleAttrRegex})
	ServiceRuleValidator.AddRule("Pattern", &validate.ValidateRule{Max: 64, Min: 1})
	ServiceRuleValidator.AddRule("Description", MicroServiceValidator.GetRule("Description"))

	FindInstanceReqValidator.AddRule("ConsumerServiceId", ServiceIdRule)
	FindInstanceReqValidator.AddRule("AppId", MicroServiceKeyValidator.GetRule("AppId"))
	FindInstanceReqValidator.AddRule("ServiceName", &validate.ValidateRule{Min: 1, Max: 128, Regexp: serviceNameForFindRegex})
	FindInstanceReqValidator.AddRule("VersionRule", versionFuzzyRule)
	FindInstanceReqValidator.AddRule("Tags", TagRule)

	GetInstanceValidator.AddRule("ConsumerServiceId", ServiceIdRule)
	GetInstanceValidator.AddRule("ProviderServiceId", ServiceIdRule)
	GetInstanceValidator.AddRule("ProviderInstanceId", &validate.ValidateRule{Min: 1, Max: 64, Regexp: simpleNameAllowEmptyRegex})
	GetInstanceValidator.AddRule("Tags", TagRule)
}

func Validate(v interface{}) error {
	if v == nil {
		util.Logger().Errorf(nil, "Data is nil!")
		return errors.New("Data is nil!")
	}
	sv := reflect.ValueOf(v)
	if sv.Kind() == reflect.Ptr && sv.IsNil() {
		util.Logger().Errorf(nil, "Pointer is nil!")
		return errors.New("Pointer is nil!")
	}
	switch t := v.(type) {
	case (*pb.MicroService):
		return MicroServiceValidator.Validate(v)
	case *pb.MicroServiceInstance, *pb.UpdateInstanceStatusRequest:
		return MicroServiceInstanceValidator.Validate(v)
	case (*pb.AddOrUpdateServiceRule):
		return ServiceRuleValidator.Validate(v)
	case *pb.GetServiceRequest, *pb.UpdateServicePropsRequest,
		*pb.DeleteServiceRequest, *pb.GetDependenciesRequest,
		*pb.GetAllSchemaRequest:
		return GetServiceReqValidator.Validate(v)
	case *pb.AddServiceTagsRequest, *pb.DeleteServiceTagsRequest,
		*pb.UpdateServiceTagRequest, *pb.GetServiceTagsRequest:
		return TagReqValidator.Validate(v)
	case *pb.GetSchemaRequest, *pb.DeleteSchemaRequest:
		return GetSchemaReqValidator.Validate(v)
	case *pb.ModifySchemaRequest:
		return SchemaValidator.Validate(v)
	case *pb.ModifySchemasRequest:
		return SchemasValidator.Validate(v)
	case *pb.FindInstancesRequest:
		return FindInstanceReqValidator.Validate(v)
	case *pb.GetOneInstanceRequest, *pb.GetInstancesRequest:
		return GetInstanceValidator.Validate(v)
	case *pb.GetAppsRequest:
		return MicroServiceKeyValidator.Validate(v)
	default:
		util.Logger().Errorf(nil, "No validator for %T.", t)
		return nil
	}
}
<commit_msg>description support "<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 core

import (
	"errors"
	"github.com/ServiceComb/service-center/pkg/util"
	"github.com/ServiceComb/service-center/pkg/validate"
	pb "github.com/ServiceComb/service-center/server/core/proto"
	"math"
	"reflect"
	"regexp"
)

var (
	ServiceAPI  pb.ServiceCtrlServer
	InstanceAPI pb.SerivceInstanceCtrlServerEx

	VersionRegex                  *regexp.Regexp
	MicroServiceValidator         validate.Validator
	MicroServiceInstanceValidator validate.Validator
	ServiceRuleValidator          validate.Validator
	ServicePathValidator          validate.Validator
	HealthCheckInfoValidator      validate.Validator
	MicroServiceKeyValidator      validate.Validator
	DataCenterInfoValidator       validate.Validator
	GetMSExistsReqValidator       validate.Validator
	GetSchemaExistsReqValidator   validate.Validator
	GetServiceReqValidator        validate.Validator
	GetSchemaReqValidator         validate.Validator
	ConsumerMsValidator           validate.Validator
	ProviderMsValidator           validate.Validator
	TagReqValidator               validate.Validator
	FindInstanceReqValidator      validate.Validator
	GetInstanceValidator          validate.Validator
	SchemasValidator              validate.Validator
	SchemaValidator               validate.Validator
	FrameWKValidator              validate.Validator

	SchemaIdRule *validate.ValidateRule
	TagRule      *validate.ValidateRule
)

func init() {
	// 非map/slice的validator
	nameRegex, _ := regexp.Compile(`^[a-zA-Z0-9]*$|^[a-zA-Z0-9][a-zA-Z0-9_\-.]*[a-zA-Z0-9]$`)
	serviceNameForFindRegex, _ := regexp.Compile(`^[a-zA-Z0-9]*$|^[a-zA-Z0-9][a-zA-Z0-9_\-.:]*[a-zA-Z0-9]$`)
	//name模糊规则: name, *
	nameFuzzyRegex, _ := regexp.Compile(`^[a-zA-Z0-9]*$|^[a-zA-Z0-9][a-zA-Z0-9_\-.]*[a-zA-Z0-9]$|^\*$`)
	VersionRegex, _ = regexp.Compile(`^[0-9]+(\.[0-9]+){0,2}$`)
	// version模糊规则: 1.0, 1.0+, 1.0-2.0, latest
	versionFuzzyRegex, _ := regexp.Compile(`^[0-9]*$|^[0-9]+(\.[0-9]+)*\+{0,1}$|^[0-9]+(\.[0-9]+)*-[0-9]+(\.[0-9]+)*$|^latest$`)
	pathRegex, _ := regexp.Compile(`^[A-Za-z0-9.,?'\\/+&%$#=~_\-@{}]*$`)
	descriptionRegex, _ := regexp.Compile(`^[\p{Han}\w\s。.:*,\-：”“"]*$`)
	levelRegex, _ := regexp.Compile(`^(FRONT|MIDDLE|BACK)$`)
	statusRegex, _ := regexp.Compile("^(" + pb.MS_UP + "|" + pb.MS_DOWN + ")*$")
	serviceIdRegex, _ := regexp.Compile(`^.*$`)
	aliasRegex, _ := regexp.Compile(`^[a-zA-Z0-9_\-.:]*$`)
	frameversionRegex, _ := regexp.Compile(`^[a-zA-Z0-9_\-.]*$`)
	registerByRegex, _ := regexp.Compile("^(" + util.StringJoin([]string{
		pb.REGISTERBY_SDK, pb.REGISTERBY_PLATFORM, pb.REGISTERBY_SIDECAR, pb.REGISTERBY_UNKNOWM}, "|") + ")*$")
	envRegex, _ := regexp.Compile("^(" + util.StringJoin([]string{
		pb.ENV_DEV, pb.ENV_TEST, pb.ENV_ACCEPT, pb.ENV_PROD}, "|") + ")*$")
	// map/slice元素的validator
	// 元素的格式和长度由正则控制
	// map/slice的长度由validator中的min/max/length控制
	schemaIdRegex, _ := regexp.Compile(`^[a-zA-Z0-9]{1,160}$|^[a-zA-Z0-9][a-zA-Z0-9_\-.]{0,158}[a-zA-Z0-9]$`) //length:{1,160}
	instStatusRegex, _ := regexp.Compile("^(" + util.StringJoin([]string{
		pb.MSI_UP, pb.MSI_DOWN, pb.MSI_STARTING, pb.MSI_OUTOFSERVICE}, "|") + ")$")
	tagRegex, _ := regexp.Compile(`^[a-zA-Z][a-zA-Z0-9_\-.]{0,63}$`)
	hbModeRegex, _ := regexp.Compile(`^(push|pull)$`)
	numberAllowEmptyRegex, _ := regexp.Compile(`^[0-9]*$`)
	numberRegex, _ := regexp.Compile(`^[0-9]+$`)
	epRegex, _ := regexp.Compile(`^[A-Za-z0-9:/?=&%_.-]+$`)
	simpleNameAllowEmptyRegex, _ := regexp.Compile(`^[A-Za-z0-9_.-]*$`)
	simpleNameRegex, _ := regexp.Compile(`^[A-Za-z0-9_.-]+$`)
	regionRegex, _ := regexp.Compile(`^[A-Za-z0-9_.-]+$`)
	ruleRegex, _ := regexp.Compile(`^(WHITE|BLACK)$`)
	ruleAttrRegex, _ := regexp.Compile(`((^tag_[a-zA-Z][a-zA-Z0-9_\-.]{0,63}$)|(^ServiceId$)|(^AppId$)|(^ServiceName$)|(^Version$)|(^Description$)|(^Level$)|(^Status$))`)
	SchemaSummaryRegex, _ := regexp.Compile(`(a-zA-Z0-9)*`)

	ServiceIdRule := &validate.ValidateRule{Min: 1, Length: 64, Regexp: serviceIdRegex}
	InstanceStatusRule := &validate.ValidateRule{Regexp: instStatusRegex}
	SchemaIdRule = &validate.ValidateRule{Regexp: schemaIdRegex}
	nameRule := &validate.ValidateRule{Min: 1, Max: 128, Regexp: nameRegex}
	versionFuzzyRule := &validate.ValidateRule{Min: 1, Max: 128, Regexp: versionFuzzyRegex}
	TagRule = &validate.ValidateRule{Regexp: tagRegex}

	MicroServiceKeyValidator.AddRule("Environment", &validate.ValidateRule{Regexp: envRegex})
	MicroServiceKeyValidator.AddRule("AppId", &validate.ValidateRule{Min: 1, Max: 160, Regexp: nameRegex})
	MicroServiceKeyValidator.AddRule("ServiceName", nameRule)
	MicroServiceKeyValidator.AddRule("Version", &validate.ValidateRule{Min: 1, Max: 64, Regexp: VersionRegex})

	ServicePathValidator.AddRule("Path", &validate.ValidateRule{Regexp: pathRegex})

	FrameWKValidator.AddRule("Name", &validate.ValidateRule{Min: 1, Max: 64, Regexp: nameRegex})
	FrameWKValidator.AddRule("Version", &validate.ValidateRule{Length: 64, Regexp: frameversionRegex})

	MicroServiceValidator.AddRules(MicroServiceKeyValidator.GetRules())
	MicroServiceValidator.AddRule("Description", &validate.ValidateRule{Length: 256, Regexp: descriptionRegex})
	MicroServiceValidator.AddRule("Level", &validate.ValidateRule{Min: 1, Regexp: levelRegex})
	MicroServiceValidator.AddRule("Status", &validate.ValidateRule{Min: 1, Regexp: statusRegex})
	MicroServiceValidator.AddRule("Schemas", SchemaIdRule)
	MicroServiceValidator.AddSub("Paths", &ServicePathValidator)
	MicroServiceValidator.AddRule("Alias", &validate.ValidateRule{Length: 128, Regexp: aliasRegex})
	MicroServiceValidator.AddRule("RegisterBy", &validate.ValidateRule{Min: 1, Length: 64, Regexp: registerByRegex})
	MicroServiceValidator.AddSub("Framework", &FrameWKValidator)

	GetMSExistsReqValidator.AddRules(MicroServiceKeyValidator.GetRules())
	GetMSExistsReqValidator.AddRule("Version", versionFuzzyRule)

	GetSchemaExistsReqValidator.AddRule("ServiceId", ServiceIdRule)
	GetSchemaExistsReqValidator.AddRule("SchemaId", SchemaIdRule)

	var subSchemaValidor validate.Validator
	subSchemaValidor.AddRule("SchemaId", SchemaIdRule)
	subSchemaValidor.AddRule("Summary", &validate.ValidateRule{Min: 1, Max: 512, Regexp: SchemaSummaryRegex})
	subSchemaValidor.AddRule("Schema", &validate.ValidateRule{Min: 1})

	SchemasValidator.AddRule("ServiceId", ServiceIdRule)
	SchemasValidator.AddSub("Schemas", &subSchemaValidor)

	SchemaValidator.AddRules(subSchemaValidor.GetRules())
	SchemaValidator.AddRule("ServiceId", ServiceIdRule)
	SchemaValidator.AddRule("Summary", &validate.ValidateRule{Max: 512, Regexp: SchemaSummaryRegex})

	GetServiceReqValidator.AddRule("ServiceId", ServiceIdRule)

	GetSchemaReqValidator.AddRule("ServiceId", ServiceIdRule)
	GetSchemaReqValidator.AddRule("SchemaId", SchemaIdRule)

	ConsumerMsValidator.AddRules(MicroServiceKeyValidator.GetRules())

	ProviderMsValidator.AddRules(MicroServiceKeyValidator.GetRules())
	ProviderMsValidator.AddRule("ServiceName", &validate.ValidateRule{Min: 1, Max: 128, Regexp: nameFuzzyRegex})
	ProviderMsValidator.AddRule("Version", versionFuzzyRule)

	TagReqValidator.AddRule("ServiceId", ServiceIdRule)
	TagReqValidator.AddRule("Tags", TagRule)
	TagReqValidator.AddRule("Keys", &validate.ValidateRule{Regexp: tagRegex})
	TagReqValidator.AddRule("Key", &validate.ValidateRule{Regexp: tagRegex})
	TagReqValidator.AddRule("Value", &validate.ValidateRule{Regexp: tagRegex})

	HealthCheckInfoValidator.AddRule("Mode", &validate.ValidateRule{Regexp: hbModeRegex})
	HealthCheckInfoValidator.AddRule("Port", &validate.ValidateRule{Max: math.MaxInt16, Regexp: numberAllowEmptyRegex})
	HealthCheckInfoValidator.AddRule("Times", &validate.ValidateRule{Max: math.MaxInt32, Regexp: numberRegex})
	HealthCheckInfoValidator.AddRule("Interval", &validate.ValidateRule{Max: math.MaxInt32, Regexp: numberRegex})
	HealthCheckInfoValidator.AddRule("Url", &validate.ValidateRule{Regexp: pathRegex})

	MicroServiceInstanceValidator.AddRule("InstanceId", &validate.ValidateRule{Length: 64, Regexp: simpleNameAllowEmptyRegex})
	MicroServiceInstanceValidator.AddRule("ServiceId", ServiceIdRule)
	MicroServiceInstanceValidator.AddRule("Endpoints", &validate.ValidateRule{Regexp: epRegex})
	MicroServiceInstanceValidator.AddRule("HostName", &validate.ValidateRule{Length: 64, Regexp: simpleNameRegex})
	MicroServiceInstanceValidator.AddSub("HealthCheck", &HealthCheckInfoValidator)
	MicroServiceInstanceValidator.AddRule("Status", InstanceStatusRule)
	MicroServiceInstanceValidator.AddSub("DataCenterInfo", &DataCenterInfoValidator)

	DataCenterInfoValidator.AddRule("Name", &validate.ValidateRule{Length: 128, Regexp: simpleNameRegex})
	DataCenterInfoValidator.AddRule("Region", &validate.ValidateRule{Length: 128, Regexp: regionRegex})
	DataCenterInfoValidator.AddRule("AvailableZone", &validate.ValidateRule{Length: 128, Regexp: regionRegex})

	ServiceRuleValidator.AddRule("RuleType", &validate.ValidateRule{Regexp: ruleRegex})
	ServiceRuleValidator.AddRule("Attribute", &validate.ValidateRule{Regexp: ruleAttrRegex})
	ServiceRuleValidator.AddRule("Pattern", &validate.ValidateRule{Max: 64, Min: 1})
	ServiceRuleValidator.AddRule("Description", MicroServiceValidator.GetRule("Description"))

	FindInstanceReqValidator.AddRule("ConsumerServiceId", ServiceIdRule)
	FindInstanceReqValidator.AddRule("AppId", MicroServiceKeyValidator.GetRule("AppId"))
	FindInstanceReqValidator.AddRule("ServiceName", &validate.ValidateRule{Min: 1, Max: 128, Regexp: serviceNameForFindRegex})
	FindInstanceReqValidator.AddRule("VersionRule", versionFuzzyRule)
	FindInstanceReqValidator.AddRule("Tags", TagRule)

	GetInstanceValidator.AddRule("ConsumerServiceId", ServiceIdRule)
	GetInstanceValidator.AddRule("ProviderServiceId", ServiceIdRule)
	GetInstanceValidator.AddRule("ProviderInstanceId", &validate.ValidateRule{Min: 1, Max: 64, Regexp: simpleNameAllowEmptyRegex})
	GetInstanceValidator.AddRule("Tags", TagRule)
}

func Validate(v interface{}) error {
	if v == nil {
		util.Logger().Errorf(nil, "Data is nil!")
		return errors.New("Data is nil!")
	}
	sv := reflect.ValueOf(v)
	if sv.Kind() == reflect.Ptr && sv.IsNil() {
		util.Logger().Errorf(nil, "Pointer is nil!")
		return errors.New("Pointer is nil!")
	}
	switch t := v.(type) {
	case (*pb.MicroService):
		return MicroServiceValidator.Validate(v)
	case *pb.MicroServiceInstance, *pb.UpdateInstanceStatusRequest:
		return MicroServiceInstanceValidator.Validate(v)
	case (*pb.AddOrUpdateServiceRule):
		return ServiceRuleValidator.Validate(v)
	case *pb.GetServiceRequest, *pb.UpdateServicePropsRequest,
		*pb.DeleteServiceRequest, *pb.GetDependenciesRequest,
		*pb.GetAllSchemaRequest:
		return GetServiceReqValidator.Validate(v)
	case *pb.AddServiceTagsRequest, *pb.DeleteServiceTagsRequest,
		*pb.UpdateServiceTagRequest, *pb.GetServiceTagsRequest:
		return TagReqValidator.Validate(v)
	case *pb.GetSchemaRequest, *pb.DeleteSchemaRequest:
		return GetSchemaReqValidator.Validate(v)
	case *pb.ModifySchemaRequest:
		return SchemaValidator.Validate(v)
	case *pb.ModifySchemasRequest:
		return SchemasValidator.Validate(v)
	case *pb.FindInstancesRequest:
		return FindInstanceReqValidator.Validate(v)
	case *pb.GetOneInstanceRequest, *pb.GetInstancesRequest:
		return GetInstanceValidator.Validate(v)
	case *pb.GetAppsRequest:
		return MicroServiceKeyValidator.Validate(v)
	default:
		util.Logger().Errorf(nil, "No validator for %T.", t)
		return nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package build

// TODO(adg): test authentication

import (
	"appengine"
	"appengine/datastore"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"time"
)

func init() {
	http.HandleFunc("/buildtest", testHandler)
}

var testEntityKinds = []string{
	"Package",
	"Commit",
	"Result",
	"Log",
}

const testPkg = "code.google.com/p/go.test"

var testPackage = &Package{Name: "Test", Kind: "subrepo", Path: testPkg}

var testPackages = []*Package{
	{Name: "Go", Path: ""},
	testPackage,
}

var tCommitTime = time.Now().Add(-time.Hour * 24 * 7)

func tCommit(hash, parentHash string) *Commit {
	tCommitTime.Add(time.Hour) // each commit should have a different time
	return &Commit{
		Hash:       hash,
		ParentHash: parentHash,
		Time:       tCommitTime,
		User:       "adg",
		Desc:       "change description",
	}
}

var testRequests = []struct {
	path string
	vals url.Values
	req  interface{}
	res  interface{}
}{
	// Packages
	{"/packages?kind=subrepo", nil, nil, []*Package{testPackage}},

	// Go repo
	{"/commit", nil, tCommit("0001", "0000"), nil},
	{"/commit", nil, tCommit("0002", "0001"), nil},
	{"/commit", nil, tCommit("0003", "0002"), nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0001", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0002", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},

	// multiple builders
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/result", nil, &Result{Builder: "linux-amd64", Hash: "0003", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0002"}}},

	// branches
	{"/commit", nil, tCommit("0004", "0003"), nil},
	{"/commit", nil, tCommit("0005", "0002"), nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}},
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0005", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0004"}}},
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0004", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},

	// logs
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false, Log: "test"}, nil},
	{"/log/a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", nil, nil, "test"},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, nil},

	// repeat failure (shouldn't re-send mail)
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false, Log: "test"}, nil},

	// non-Go repos
	{"/commit", nil, &Commit{PackagePath: testPkg, Hash: "1001", ParentHash: "1000"}, nil},
	{"/commit", nil, &Commit{PackagePath: testPkg, Hash: "1002", ParentHash: "1001"}, nil},
	{"/commit", nil, &Commit{PackagePath: testPkg, Hash: "1003", ParentHash: "1002"}, nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1003"}}},
	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1003", GoHash: "0001", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1002"}}},
	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1002", GoHash: "0001", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1001"}}},
	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0001", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0002"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1003"}}},

	// re-build Go revision for stale subrepos
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}},
	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0005", OK: false, Log: "boo"}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, nil},
}

func testHandler(w http.ResponseWriter, r *http.Request) {
	if !appengine.IsDevAppServer() {
		fmt.Fprint(w, "These tests must be run under the dev_appserver.")
		return
	}
	c := appengine.NewContext(r)
	if err := nukeEntities(c, testEntityKinds); err != nil {
		logErr(w, r, err)
		return
	}
	if r.FormValue("nukeonly") != "" {
		fmt.Fprint(w, "OK")
		return
	}

	for _, p := range testPackages {
		if _, err := datastore.Put(c, p.Key(c), p); err != nil {
			logErr(w, r, err)
			return
		}
	}

	for i, t := range testRequests {
		c.Infof("running test %d %s", i, t.path)
		errorf := func(format string, args ...interface{}) {
			fmt.Fprintf(w, "%d %s: ", i, t.path)
			fmt.Fprintf(w, format, args...)
			fmt.Fprintln(w)
		}
		var body io.ReadWriter
		if t.req != nil {
			body = new(bytes.Buffer)
			json.NewEncoder(body).Encode(t.req)
		}
		url := "http://" + domain + t.path
		if t.vals != nil {
			url += "?" + t.vals.Encode()
		}
		req, err := http.NewRequest("POST", url, body)
		if err != nil {
			logErr(w, r, err)
			return
		}
		if t.req != nil {
			req.Method = "POST"
		}
		req.Header = r.Header
		rec := httptest.NewRecorder()

		// Make the request
		http.DefaultServeMux.ServeHTTP(rec, req)

		if rec.Code != 0 && rec.Code != 200 {
			errorf(rec.Body.String())
			return
		}
		resp := new(dashResponse)

		// If we're expecting a *Todo value,
		// prime the Response field with a Todo and a Commit inside it.
		if _, ok := t.res.(*Todo); ok {
			resp.Response = &Todo{Data: &Commit{}}
		}

		if strings.HasPrefix(t.path, "/log/") {
			resp.Response = rec.Body.String()
		} else {
			err := json.NewDecoder(rec.Body).Decode(resp)
			if err != nil {
				errorf("decoding response: %v", err)
				return
			}
		}
		if e, ok := t.res.(string); ok {
			g, ok := resp.Response.(string)
			if !ok {
				errorf("Response not string: %T", resp.Response)
				return
			}
			if g != e {
				errorf("response mismatch: got %q want %q", g, e)
				return
			}
		}
		if e, ok := t.res.(*Todo); ok {
			g, ok := resp.Response.(*Todo)
			if !ok {
				errorf("Response not *Todo: %T", resp.Response)
				return
			}
			if e.Data == nil && g.Data != nil {
				errorf("Response.Data should be nil, got: %v", g.Data)
				return
			}
			if g.Data == nil {
				errorf("Response.Data is nil, want: %v", e.Data)
				return
			}
			gd, ok := g.Data.(*Commit)
			if !ok {
				errorf("Response.Data not *Commit: %T", g.Data)
				return
			}
			if eh := e.Data.(*Commit).Hash; eh != gd.Hash {
				errorf("hashes don't match: got %q, want %q", gd.Hash, eh)
				return
			}
		}
		if t.res == nil && resp.Response != nil {
			errorf("response mismatch: got %q expected <nil>",
				resp.Response)
			return
		}
	}
	fmt.Fprint(w, "PASS")
}

func nukeEntities(c appengine.Context, kinds []string) error {
	if !appengine.IsDevAppServer() {
		return errors.New("can't nuke production data")
	}
	var keys []*datastore.Key
	for _, kind := range kinds {
		q := datastore.NewQuery(kind).KeysOnly()
		for t := q.Run(c); ; {
			k, err := t.Next(nil)
			if err == datastore.Done {
				break
			}
			if err != nil {
				return err
			}
			keys = append(keys, k)
		}
	}
	return datastore.DeleteMulti(c, keys)
}
<commit_msg>misc/dashboard/app: fix tests<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package build

// TODO(adg): test authentication

import (
	"appengine"
	"appengine/datastore"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"time"
)

func init() {
	http.HandleFunc("/buildtest", testHandler)
}

var testEntityKinds = []string{
	"Package",
	"Commit",
	"Result",
	"Log",
}

const testPkg = "code.google.com/p/go.test"

var testPackage = &Package{Name: "Test", Kind: "subrepo", Path: testPkg}

var testPackages = []*Package{
	{Name: "Go", Path: ""},
	testPackage,
}

var tCommitTime = time.Now().Add(-time.Hour * 24 * 7)

func tCommit(hash, parentHash, path string) *Commit {
	tCommitTime.Add(time.Hour) // each commit should have a different time
	return &Commit{
		PackagePath: path,
		Hash:        hash,
		ParentHash:  parentHash,
		Time:        tCommitTime,
		User:        "adg",
		Desc:        "change description " + hash,
	}
}

var testRequests = []struct {
	path string
	vals url.Values
	req  interface{}
	res  interface{}
}{
	// Packages
	{"/packages?kind=subrepo", nil, nil, []*Package{testPackage}},

	// Go repo
	{"/commit", nil, tCommit("0001", "0000", ""), nil},
	{"/commit", nil, tCommit("0002", "0001", ""), nil},
	{"/commit", nil, tCommit("0003", "0002", ""), nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0001", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0002", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},

	// multiple builders
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/result", nil, &Result{Builder: "linux-amd64", Hash: "0003", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-amd64"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0002"}}},

	// branches
	{"/commit", nil, tCommit("0004", "0003", ""), nil},
	{"/commit", nil, tCommit("0005", "0002", ""), nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}},
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0005", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0004"}}},
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0004", OK: false}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0003"}}},

	// logs
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false, Log: "test"}, nil},
	{"/log/a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", nil, nil, "test"},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, nil},

	// repeat failure (shouldn't re-send mail)
	{"/result", nil, &Result{Builder: "linux-386", Hash: "0003", OK: false, Log: "test"}, nil},

	// non-Go repos
	{"/commit", nil, tCommit("1001", "1000", testPkg), nil},
	{"/commit", nil, tCommit("1002", "1001", testPkg), nil},
	{"/commit", nil, tCommit("1003", "1002", testPkg), nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1003"}}},
	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1003", GoHash: "0001", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1002"}}},
	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1002", GoHash: "0001", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1001"}}},
	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0001", OK: true}, nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0001"}}, nil, nil},
	{"/todo", url.Values{"kind": {"build-package"}, "builder": {"linux-386"}, "packagePath": {testPkg}, "goHash": {"0002"}}, nil, &Todo{Kind: "build-package", Data: &Commit{Hash: "1003"}}},

	// re-build Go revision for stale subrepos
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, &Todo{Kind: "build-go-commit", Data: &Commit{Hash: "0005"}}},
	{"/result", nil, &Result{PackagePath: testPkg, Builder: "linux-386", Hash: "1001", GoHash: "0005", OK: false, Log: "boo"}, nil},
	{"/todo", url.Values{"kind": {"build-go-commit"}, "builder": {"linux-386"}}, nil, nil},
}

func testHandler(w http.ResponseWriter, r *http.Request) {
	if !appengine.IsDevAppServer() {
		fmt.Fprint(w, "These tests must be run under the dev_appserver.")
		return
	}
	c := appengine.NewContext(r)
	if err := nukeEntities(c, testEntityKinds); err != nil {
		logErr(w, r, err)
		return
	}
	if r.FormValue("nukeonly") != "" {
		fmt.Fprint(w, "OK")
		return
	}

	for _, p := range testPackages {
		if _, err := datastore.Put(c, p.Key(c), p); err != nil {
			logErr(w, r, err)
			return
		}
	}

	for i, t := range testRequests {
		c.Infof("running test %d %s", i, t.path)
		errorf := func(format string, args ...interface{}) {
			fmt.Fprintf(w, "%d %s: ", i, t.path)
			fmt.Fprintf(w, format, args...)
			fmt.Fprintln(w)
		}
		var body io.ReadWriter
		if t.req != nil {
			body = new(bytes.Buffer)
			json.NewEncoder(body).Encode(t.req)
		}
		url := "http://" + domain + t.path
		if t.vals != nil {
			url += "?" + t.vals.Encode()
		}
		req, err := http.NewRequest("POST", url, body)
		if err != nil {
			logErr(w, r, err)
			return
		}
		if t.req != nil {
			req.Method = "POST"
		}
		req.Header = r.Header
		rec := httptest.NewRecorder()

		// Make the request
		http.DefaultServeMux.ServeHTTP(rec, req)

		if rec.Code != 0 && rec.Code != 200 {
			errorf(rec.Body.String())
			return
		}
		resp := new(dashResponse)

		// If we're expecting a *Todo value,
		// prime the Response field with a Todo and a Commit inside it.
		if _, ok := t.res.(*Todo); ok {
			resp.Response = &Todo{Data: &Commit{}}
		}

		if strings.HasPrefix(t.path, "/log/") {
			resp.Response = rec.Body.String()
		} else {
			err := json.NewDecoder(rec.Body).Decode(resp)
			if err != nil {
				errorf("decoding response: %v", err)
				return
			}
		}
		if e, ok := t.res.(string); ok {
			g, ok := resp.Response.(string)
			if !ok {
				errorf("Response not string: %T", resp.Response)
				return
			}
			if g != e {
				errorf("response mismatch: got %q want %q", g, e)
				return
			}
		}
		if e, ok := t.res.(*Todo); ok {
			g, ok := resp.Response.(*Todo)
			if !ok {
				errorf("Response not *Todo: %T", resp.Response)
				return
			}
			if e.Data == nil && g.Data != nil {
				errorf("Response.Data should be nil, got: %v", g.Data)
				return
			}
			if g.Data == nil {
				errorf("Response.Data is nil, want: %v", e.Data)
				return
			}
			gd, ok := g.Data.(*Commit)
			if !ok {
				errorf("Response.Data not *Commit: %T", g.Data)
				return
			}
			if eh := e.Data.(*Commit).Hash; eh != gd.Hash {
				errorf("hashes don't match: got %q, want %q", gd.Hash, eh)
				return
			}
		}
		if t.res == nil && resp.Response != nil {
			errorf("response mismatch: got %q expected <nil>",
				resp.Response)
			return
		}
	}
	fmt.Fprint(w, "PASS\nYou should see only one mail notification (for 0003/linux-386) in the dev_appserver logs.")
}

func nukeEntities(c appengine.Context, kinds []string) error {
	if !appengine.IsDevAppServer() {
		return errors.New("can't nuke production data")
	}
	var keys []*datastore.Key
	for _, kind := range kinds {
		q := datastore.NewQuery(kind).KeysOnly()
		for t := q.Run(c); ; {
			k, err := t.Next(nil)
			if err == datastore.Done {
				break
			}
			if err != nil {
				return err
			}
			keys = append(keys, k)
		}
	}
	return datastore.DeleteMulti(c, keys)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"github.com/weaveworks/flux"
	"github.com/weaveworks/flux/api"
	transport "github.com/weaveworks/flux/http"
	"github.com/weaveworks/flux/http/client"
)

type rootOpts struct {
	URL   string
	Token string
	API   api.ClientService
}

// fluxctl never sends an instance ID directly; it's always blank, and
// optionally gets populated by an intermediating authfe from the token.
const noInstanceID = flux.InstanceID("")

type serviceOpts struct {
	*rootOpts
}

func newService(parent *rootOpts) *serviceOpts {
	return &serviceOpts{rootOpts: parent}
}

func newRoot() *rootOpts {
	return &rootOpts{}
}

var rootLongHelp = strings.TrimSpace(`
fluxctl helps you deploy your code.

Workflow:
  fluxctl list-services                                        # Which services are running?
  fluxctl list-images --service=default/foo                    # Which images are running/available?
  fluxctl release --service=default/foo --update-image=bar:v2  # Release new version.
`)

const envVariableURL = "FLUX_URL"
const envVariableToken = "FLUX_SERVICE_TOKEN"

func (opts *rootOpts) Command() *cobra.Command {
	cmd := &cobra.Command{
		Use:               "fluxctl",
		Long:              rootLongHelp,
		SilenceUsage:      true,
		SilenceErrors:     true,
		PersistentPreRunE: opts.PersistentPreRunE,
	}
	cmd.PersistentFlags().StringVarP(&opts.URL, "url", "u", "https://cloud.weave.works/api/flux",
		fmt.Sprintf("base URL of the flux service; you can also set the environment variable %s", envVariableURL))
	cmd.PersistentFlags().StringVarP(&opts.Token, "token", "t", "",
		fmt.Sprintf("Weave Cloud service token; you can also set the environment variable %s", envVariableToken))

	svcopts := newService(opts)

	cmd.AddCommand(
		newVersionCommand(),
		newServiceShow(svcopts).Command(),
		newServiceList(svcopts).Command(),
		newServiceRelease(svcopts).Command(),
		newServiceAutomate(svcopts).Command(),
		newServiceDeautomate(svcopts).Command(),
		newServiceLock(svcopts).Command(),
		newServiceUnlock(svcopts).Command(),
		newSave(opts).Command(),
		newIdentity(opts).Command(),
	)

	return cmd
}

func (opts *rootOpts) PersistentPreRunE(cmd *cobra.Command, _ []string) error {
	opts.URL = getFromEnvIfNotSet(cmd.Flags(), "url", envVariableURL, opts.URL)
	if _, err := url.Parse(opts.URL); err != nil {
		return errors.Wrapf(err, "parsing URL")
	}
	opts.Token = getFromEnvIfNotSet(cmd.Flags(), "token", envVariableToken, opts.Token)
	opts.API = client.New(http.DefaultClient, transport.NewServiceRouter(), opts.URL, flux.Token(opts.Token))
	return nil
}

func getFromEnvIfNotSet(flags *pflag.FlagSet, flagName, envName, value string) string {
	if flags.Changed(flagName) {
		return value
	}
	if env := os.Getenv(envName); env != "" {
		return env
	}
	return value // not changed, so presumably the default
}
<commit_msg>Add WEAVE_CLOUD_TOKEN alias for FLUX_SERVICE_TOKEN<commit_after>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"github.com/weaveworks/flux"
	"github.com/weaveworks/flux/api"
	transport "github.com/weaveworks/flux/http"
	"github.com/weaveworks/flux/http/client"
)

type rootOpts struct {
	URL   string
	Token string
	API   api.ClientService
}

// fluxctl never sends an instance ID directly; it's always blank, and
// optionally gets populated by an intermediating authfe from the token.
const noInstanceID = flux.InstanceID("")

type serviceOpts struct {
	*rootOpts
}

func newService(parent *rootOpts) *serviceOpts {
	return &serviceOpts{rootOpts: parent}
}

func newRoot() *rootOpts {
	return &rootOpts{}
}

var rootLongHelp = strings.TrimSpace(`
fluxctl helps you deploy your code.

Workflow:
  fluxctl list-services                                        # Which services are running?
  fluxctl list-images --service=default/foo                    # Which images are running/available?
  fluxctl release --service=default/foo --update-image=bar:v2  # Release new version.
`)

const (
	envVariableURL        = "FLUX_URL"
	envVariableToken      = "FLUX_SERVICE_TOKEN"
	envVariableCloudToken = "WEAVE_CLOUD_TOKEN"
)

func (opts *rootOpts) Command() *cobra.Command {
	cmd := &cobra.Command{
		Use:               "fluxctl",
		Long:              rootLongHelp,
		SilenceUsage:      true,
		SilenceErrors:     true,
		PersistentPreRunE: opts.PersistentPreRunE,
	}
	cmd.PersistentFlags().StringVarP(&opts.URL, "url", "u", "https://cloud.weave.works/api/flux",
		fmt.Sprintf("base URL of the flux service; you can also set the environment variable %s", envVariableURL))
	cmd.PersistentFlags().StringVarP(&opts.Token, "token", "t", "",
		fmt.Sprintf("Weave Cloud service token; you can also set the environment variable %s or %s", envVariableCloudToken, envVariableToken))

	svcopts := newService(opts)

	cmd.AddCommand(
		newVersionCommand(),
		newServiceShow(svcopts).Command(),
		newServiceList(svcopts).Command(),
		newServiceRelease(svcopts).Command(),
		newServiceAutomate(svcopts).Command(),
		newServiceDeautomate(svcopts).Command(),
		newServiceLock(svcopts).Command(),
		newServiceUnlock(svcopts).Command(),
		newSave(opts).Command(),
		newIdentity(opts).Command(),
	)

	return cmd
}

func (opts *rootOpts) PersistentPreRunE(cmd *cobra.Command, _ []string) error {
	opts.URL = getFromEnvIfNotSet(cmd.Flags(), "url", opts.URL, envVariableURL)
	if _, err := url.Parse(opts.URL); err != nil {
		return errors.Wrapf(err, "parsing URL")
	}
	opts.Token = getFromEnvIfNotSet(cmd.Flags(), "token", opts.Token, envVariableToken, envVariableCloudToken)
	opts.API = client.New(http.DefaultClient, transport.NewServiceRouter(), opts.URL, flux.Token(opts.Token))
	return nil
}

func getFromEnvIfNotSet(flags *pflag.FlagSet, flagName, value string, envNames ...string) string {
	if flags.Changed(flagName) {
		return value
	}
	for _, envName := range envNames {
		if env := os.Getenv(envName); env != "" {
			return env
		}
	}
	return value // not changed, so presumably the default
}
<|endoftext|>


<commit_before>package server

import (
  "net/http"

  "golang.org/x/net/context"
  "github.com/grpc-ecosystem/grpc-gateway/runtime"
  "google.golang.org/grpc"
  gw "github.com/iamneal/book_parser/server/proto"
)

func Run(rpcLoc, httpLoc string) error {
  ctx := context.Background()
  ctx, cancel := context.WithCancel(ctx)
  defer cancel()

  mux := runtime.NewServeMux()
  opts := []grpc.DialOption{grpc.WithInsecure()}
  err := gw.RegisterBookParserHandlerFromEndpoint(ctx, mux, rpcLoc, opts)
  if err != nil {
    return err
  }
	httpMux := http.NewServeMux()
	httpMux.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
		res.WriteHeader(http.StatusOK)
		res.Write([]byte("hello world"))
	})
	httpMux.Handle("/api/",http.Handler(mux))
  return http.ListenAndServe(httpLoc, httpMux)
}

<commit_msg>prepare server for oauth2<commit_after>package server

import (
  "net/http"

  "golang.org/x/net/context"
  "github.com/grpc-ecosystem/grpc-gateway/runtime"
  "google.golang.org/grpc"
	"golang.org/x/oauth2"
  gw "github.com/iamneal/book_parser/server/proto"
)

type MyHttpServer struct {
	HttpMux *http.ServeMux
	RpcMux *runtime.ServeMux
	HttpAddr string
	RpcAddr string
	Config *oauth2.Config
	cancel context.CancelFunc
}

func NewMyHttpServer(rpcAddr, httpAddr string) (*MyHttpServer, error) {
	mhs := &MyHttpServer{
		HttpAddr: httpAddr,
		RpcAddr: rpcAddr,
	}
  ctx := context.Background()
  ctx, cancel := context.WithCancel(ctx)

	mhs.cancel = cancel

  mux := runtime.NewServeMux()
  opts := []grpc.DialOption{grpc.WithInsecure()}
  err := gw.RegisterBookParserHandlerFromEndpoint(ctx, mux, mhs.RpcAddr, opts)
  if err != nil {
    return nil, err
  }

	mhs.RpcMux = mux

	httpMux := http.NewServeMux()

	mhs.HttpMux = httpMux

	mhs.HttpMux.Handle("/api/",http.Handler(mhs.RpcMux))
	mhs.HttpMux.HandleFunc("/auth", mhs.HandleAuth)
	mhs.HttpMux.HandleFunc("/login", mhs.HandleLogin)
	mhs.HttpMux.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
		res.WriteHeader(http.StatusOK)
		jwt := req.Header.Get("driveAccessToken")
		if jwt != "" {
			res.Write([]byte("already authenticated"))
		} else {
			res.Write([]byte(`
				<html><body>
					<button href="/login"> Login with google </button>
				</body></html>
			`))
		}
	})
	return mhs, nil
}

func (mhs *MyHttpServer) Run() error {
  return http.ListenAndServe(mhs.HttpAddr, mhs.HttpMux)
}

func (mhs *MyHttpServer) Shutdown() error {
	return nil
}

func (mhs *MyHttpServer) HandleAuth(res http.ResponseWriter, req *http.Request) {

}

func (mhs *MyHttpServer) HandleLogin(res http.ResponseWriter, req *http.Request) {

}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"fmt"
	"strings"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/state/api/params"
)

// GetEnvironmentCommand is able to output either the entire environment or
// the requested value in a format of the user's choosing.
type GetEnvironmentCommand struct {
	cmd.EnvCommandBase
	key string
	out cmd.Output
}

const getEnvHelpDoc = `
If no extra args passed on the command line, all configuration keys and values
for the environment are output using the selected formatter.

A single environment value can be output by adding the environment key name to
the end of the command line.

Example:

  juju get-environment default-series  (returns the default series for the environment)
`

func (c *GetEnvironmentCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "get-environment",
		Args:    "[<environment key>]",
		Purpose: "view environment values",
		Doc:     strings.TrimSpace(getEnvHelpDoc),
		Aliases: []string{"get-env"},
	}
}

func (c *GetEnvironmentCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
}

func (c *GetEnvironmentCommand) Init(args []string) (err error) {
	c.key, err = cmd.ZeroOrOneArgs(args)
	return
}

// environmentGet1dot16 runs matches client.EnvironmentGet using a direct DB
// connection to maintain compatibility with an API server running 1.16 or
// older (when EnvironmentGet was not available). This fallback can be removed
// when we no longer maintain 1.16 compatibility.
func (c *GetEnvironmentCommand) environmentGet1dot16() (map[string]interface{}, error) {
	conn, err := juju.NewConnFromName(c.EnvName)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	// Get the existing environment config from the state.
	config, err := conn.State.EnvironConfig()
	if err != nil {
		return nil, err
	}
	attrs := config.AllAttrs()
	return attrs, nil
}

func (c *GetEnvironmentCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	attrs, err := client.EnvironmentGet()
	if params.IsCodeNotImplemented(err) {
		logger.Infof("EnvironmentGet not supported by the API server, " +
			"falling back to 1.16 compatibility mode (direct DB access)")
		attrs, err = c.environmentGet1dot16()
	}
	if err != nil {
		return err
	}

	if c.key != "" {
		if value, found := attrs[c.key]; found {
			return c.out.Write(ctx, value)
		}
		return fmt.Errorf("Key %q not found in %q environment.", c.key, attrs["name"])
	}
	// If key is empty, write out the whole lot.
	return c.out.Write(ctx, attrs)
}

type attributes map[string]interface{}

// SetEnvironment
type SetEnvironmentCommand struct {
	cmd.EnvCommandBase
	values attributes
}

const setEnvHelpDoc = `
Updates the environment of a running Juju instance.  Multiple key/value pairs
can be passed on as command line arguments.
`

func (c *SetEnvironmentCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "set-environment",
		Args:    "key=[value] ...",
		Purpose: "replace environment values",
		Doc:     strings.TrimSpace(setEnvHelpDoc),
		Aliases: []string{"set-env"},
	}
}

// SetFlags handled entirely by cmd.EnvCommandBase

func (c *SetEnvironmentCommand) Init(args []string) (err error) {
	if len(args) == 0 {
		return fmt.Errorf("No key, value pairs specified")
	}
	// TODO(thumper) look to have a common library of functions for dealing
	// with key=value pairs.
	c.values = make(attributes)
	for i, arg := range args {
		bits := strings.SplitN(arg, "=", 2)
		if len(bits) < 2 {
			return fmt.Errorf(`Missing "=" in arg %d: %q`, i+1, arg)
		}
		key := bits[0]
		if key == "agent-version" {
			return fmt.Errorf("agent-version must be set via upgrade-juju")
		}
		if _, exists := c.values[key]; exists {
			return fmt.Errorf(`Key %q specified more than once`, key)
		}
		c.values[key] = bits[1]
	}
	return nil
}

// run1dot16 runs matches client.EnvironmentSet using a direct DB
// connection to maintain compatibility with an API server running 1.16 or
// older (when EnvironmentSet was not available). This fallback can be removed
// when we no longer maintain 1.16 compatibility.
// This content was copied from SetEnvironmentCommand.Run in 1.16
func (c *SetEnvironmentCommand) run1dot16() error {
	conn, err := juju.NewConnFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer conn.Close()

	// Update state config with new values
	return conn.State.UpdateEnvironConfig(c.values, nil, nil)
}

func (c *SetEnvironmentCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	err = client.EnvironmentSet(c.values)
	if params.IsCodeNotImplemented(err) {
		logger.Infof("EnvironmentSet not supported by the API server, " +
			"falling back to 1.16 compatibility mode (direct DB access)")
		err = c.run1dot16()
	}
	return err
}

// UnsetEnvironment
type UnsetEnvironmentCommand struct {
	cmd.EnvCommandBase
	keys []string
}

const unsetEnvHelpDoc = `
Set one or more the environment configuration attributes to its default value
in a running Juju instance.  Attributes without defaults are removed.
`

func (c *UnsetEnvironmentCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unset-environment",
		Args:    "<environment key> ...",
		Purpose: "unset environment values",
		Doc:     strings.TrimSpace(unsetEnvHelpDoc),
		Aliases: []string{"unset-env"},
	}
}

func (c *UnsetEnvironmentCommand) Init(args []string) (err error) {
	if len(args) == 0 {
		return fmt.Errorf("No keys specified")
	}
	c.keys = args
	return nil
}

// run1dot16 runs matches client.EnvironmentUnset using a direct DB
// connection to maintain compatibility with an API server running 1.16 or
// older (when EnvironmentUnset was not available). This fallback can be removed
// when we no longer maintain 1.16 compatibility.
func (c *UnsetEnvironmentCommand) run1dot16() error {
	conn, err := juju.NewConnFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer conn.Close()
	return conn.State.UpdateEnvironConfig(nil, c.keys, nil)
}

func (c *UnsetEnvironmentCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	err = client.EnvironmentUnset(c.keys...)
	if params.IsCodeNotImplemented(err) {
		logger.Infof("EnvironmentUnset not supported by the API server, " +
			"falling back to 1.16 compatibility mode (direct DB access)")
		err = c.run1dot16()
	}
	return err
}
<commit_msg>cmd/juju: update doc for environment-unset<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"fmt"
	"strings"

	"launchpad.net/gnuflag"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/juju"
	"launchpad.net/juju-core/state/api/params"
)

// GetEnvironmentCommand is able to output either the entire environment or
// the requested value in a format of the user's choosing.
type GetEnvironmentCommand struct {
	cmd.EnvCommandBase
	key string
	out cmd.Output
}

const getEnvHelpDoc = `
If no extra args passed on the command line, all configuration keys and values
for the environment are output using the selected formatter.

A single environment value can be output by adding the environment key name to
the end of the command line.

Example:

  juju get-environment default-series  (returns the default series for the environment)
`

func (c *GetEnvironmentCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "get-environment",
		Args:    "[<environment key>]",
		Purpose: "view environment values",
		Doc:     strings.TrimSpace(getEnvHelpDoc),
		Aliases: []string{"get-env"},
	}
}

func (c *GetEnvironmentCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
}

func (c *GetEnvironmentCommand) Init(args []string) (err error) {
	c.key, err = cmd.ZeroOrOneArgs(args)
	return
}

// environmentGet1dot16 runs matches client.EnvironmentGet using a direct DB
// connection to maintain compatibility with an API server running 1.16 or
// older (when EnvironmentGet was not available). This fallback can be removed
// when we no longer maintain 1.16 compatibility.
func (c *GetEnvironmentCommand) environmentGet1dot16() (map[string]interface{}, error) {
	conn, err := juju.NewConnFromName(c.EnvName)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	// Get the existing environment config from the state.
	config, err := conn.State.EnvironConfig()
	if err != nil {
		return nil, err
	}
	attrs := config.AllAttrs()
	return attrs, nil
}

func (c *GetEnvironmentCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	attrs, err := client.EnvironmentGet()
	if params.IsCodeNotImplemented(err) {
		logger.Infof("EnvironmentGet not supported by the API server, " +
			"falling back to 1.16 compatibility mode (direct DB access)")
		attrs, err = c.environmentGet1dot16()
	}
	if err != nil {
		return err
	}

	if c.key != "" {
		if value, found := attrs[c.key]; found {
			return c.out.Write(ctx, value)
		}
		return fmt.Errorf("Key %q not found in %q environment.", c.key, attrs["name"])
	}
	// If key is empty, write out the whole lot.
	return c.out.Write(ctx, attrs)
}

type attributes map[string]interface{}

// SetEnvironment
type SetEnvironmentCommand struct {
	cmd.EnvCommandBase
	values attributes
}

const setEnvHelpDoc = `
Updates the environment of a running Juju instance.  Multiple key/value pairs
can be passed on as command line arguments.
`

func (c *SetEnvironmentCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "set-environment",
		Args:    "key=[value] ...",
		Purpose: "replace environment values",
		Doc:     strings.TrimSpace(setEnvHelpDoc),
		Aliases: []string{"set-env"},
	}
}

// SetFlags handled entirely by cmd.EnvCommandBase

func (c *SetEnvironmentCommand) Init(args []string) (err error) {
	if len(args) == 0 {
		return fmt.Errorf("No key, value pairs specified")
	}
	// TODO(thumper) look to have a common library of functions for dealing
	// with key=value pairs.
	c.values = make(attributes)
	for i, arg := range args {
		bits := strings.SplitN(arg, "=", 2)
		if len(bits) < 2 {
			return fmt.Errorf(`Missing "=" in arg %d: %q`, i+1, arg)
		}
		key := bits[0]
		if key == "agent-version" {
			return fmt.Errorf("agent-version must be set via upgrade-juju")
		}
		if _, exists := c.values[key]; exists {
			return fmt.Errorf(`Key %q specified more than once`, key)
		}
		c.values[key] = bits[1]
	}
	return nil
}

// run1dot16 runs matches client.EnvironmentSet using a direct DB
// connection to maintain compatibility with an API server running 1.16 or
// older (when EnvironmentSet was not available). This fallback can be removed
// when we no longer maintain 1.16 compatibility.
// This content was copied from SetEnvironmentCommand.Run in 1.16
func (c *SetEnvironmentCommand) run1dot16() error {
	conn, err := juju.NewConnFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer conn.Close()

	// Update state config with new values
	return conn.State.UpdateEnvironConfig(c.values, nil, nil)
}

func (c *SetEnvironmentCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	err = client.EnvironmentSet(c.values)
	if params.IsCodeNotImplemented(err) {
		logger.Infof("EnvironmentSet not supported by the API server, " +
			"falling back to 1.16 compatibility mode (direct DB access)")
		err = c.run1dot16()
	}
	return err
}

// UnsetEnvironment
type UnsetEnvironmentCommand struct {
	cmd.EnvCommandBase
	keys []string
}

const unsetEnvHelpDoc = `
Reset one or more the environment configuration attributes to its default
value in a running Juju instance.  Attributes without defaults are removed,
and attempting to remove a required attribute with no default will result
in an error.

Multiple attributes may be removed at once; keys are space-separated.
`

func (c *UnsetEnvironmentCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "unset-environment",
		Args:    "<environment key> ...",
		Purpose: "unset environment values",
		Doc:     strings.TrimSpace(unsetEnvHelpDoc),
		Aliases: []string{"unset-env"},
	}
}

func (c *UnsetEnvironmentCommand) Init(args []string) (err error) {
	if len(args) == 0 {
		return fmt.Errorf("No keys specified")
	}
	c.keys = args
	return nil
}

// run1dot16 runs matches client.EnvironmentUnset using a direct DB
// connection to maintain compatibility with an API server running 1.16 or
// older (when EnvironmentUnset was not available). This fallback can be removed
// when we no longer maintain 1.16 compatibility.
func (c *UnsetEnvironmentCommand) run1dot16() error {
	conn, err := juju.NewConnFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer conn.Close()
	return conn.State.UpdateEnvironConfig(nil, c.keys, nil)
}

func (c *UnsetEnvironmentCommand) Run(ctx *cmd.Context) error {
	client, err := juju.NewAPIClientFromName(c.EnvName)
	if err != nil {
		return err
	}
	defer client.Close()

	err = client.EnvironmentUnset(c.keys...)
	if params.IsCodeNotImplemented(err) {
		logger.Infof("EnvironmentUnset not supported by the API server, " +
			"falling back to 1.16 compatibility mode (direct DB access)")
		err = c.run1dot16()
	}
	return err
}
<|endoftext|>


<commit_before>// Copyright © 2017 Pennock Tech, LLC.
// All rights reserved, except as granted under license.
// Licensed per file LICENSE.txt

package main // import "go.pennock.tech/ocsprenewer/cmd/ocsprenewer"

import (
	"flag"

	// AVOID IMPORTING "os" HERE: use util.go for that.

	"go.pennock.tech/ocsprenewer/renew"
)

const (
	defHTTPUserAgentProduct = "ocsprenewer"
	defHTTPUserAgentComment = "(Pennock Tech OCSP Renewer)"
)

// Should be overridden with a version number
const defHTTPUserAgent = defHTTPUserAgentProduct + " " + defHTTPUserAgentComment

// We don't use "daemon" because we don't auto-fork into background, but
// instead make ourselves easy to supervise.  If there are complaints that
// daemonization is too hard, we can consider log-file redirection and
// self-daemonization as later features.
var pflags struct {
	Persist   bool
	IfNeeded  bool
	Verbose   bool
	NotReally bool
	Version   bool
}

var renewerConfig renew.Config

func init() {
	flag.BoolVar(&pflags.Persist, "persist", false, "run in a loop, renewing as needed")
	flag.BoolVar(&pflags.Persist, "p", false, "short form of -persist")
	flag.BoolVar(&pflags.IfNeeded, "if-needed", false, "if not persisting, only renew those which need renewal per timers")
	flag.BoolVar(&pflags.Verbose, "verbose", false, "be more verbose")
	flag.BoolVar(&pflags.Verbose, "v", false, "short form of -verbose")
	flag.BoolVar(&pflags.NotReally, "not-really", false, "don't talk to remote servers, do everything else")
	flag.BoolVar(&pflags.NotReally, "n", false, "short form of -not-really")
	flag.BoolVar(&pflags.Version, "version", false, "show version and exit")

	flag.BoolVar(&renewerConfig.Immediate, "now", false, "renew immediately in persist mode")
	//TODO: flag.StringVar(&renewerConfig.HTTPStatus, "http", "", "start an HTTP status service, on given host:port spec")
	flag.BoolVar(&renewerConfig.Directories, "dirs", false, "arguments are directories containing certs")
	flag.StringVar(&renewerConfig.OutputDir, "out-dir", "./", "place files into given directory")
	flag.StringVar(&renewerConfig.Extension, "extension", ".ocsp", "create proofs in files with this extension")
	flag.Float64Var(&renewerConfig.TimerT1, "timer-t1", 0.5, "how far through staple validity period to start trying to renew")
	flag.BoolVar(&renewerConfig.AllowNonOCSPInDir, "allow-nonocsp-in-dir", false, "do not error on certs missing OCSP info")
	flag.StringVar(&renewerConfig.CertExtensions, "cert-extensions", ".crt .cert .pem", "files in dir-scan with these extensions should be certs")
}

func main() {
	flag.Parse()

	if pflags.Version {
		showVersion()
		exit(0)
	}

	renewerConfig.InputPaths = flag.Args()
	renewerConfig.HTTPUserAgent = defHTTPUserAgent

	if Version != "" {
		renewerConfig.HTTPUserAgent = defHTTPUserAgentProduct + "/" + httpVersion(Version) + " " + defHTTPUserAgentComment
	}

	renewer, err := renew.New(renewerConfig)
	if err != nil {
		stderr("configuring OCSP renewer failed: %s\n", err)
		exit(1)
	}
	if pflags.Verbose {
		renewer.SetLogLevel(1)
	}
	if pflags.NotReally {
		renewer.SetNotReally(true)
	}

	if err := renewer.BasicChecks(); err != nil {
		renewer.Logf("initial startup checks failed: %s", err)
		exit(1)
	}

	if pflags.Persist {
		renewer.Logf("%s: starting persistent run, version %s", ProjectName, Version)
		renewer.Logf("argv: %s", argvQuoted())
		setupSignals(renewer)
		// Should not return until exiting
		ok := renewer.Start()
		if ok {
			exit(0)
		}
		exit(1)
	}

	renewer.LogAtf(1, "%s: one-shot renewr run, version %s", ProjectName, Version)

	if pflags.IfNeeded {
		renewer.SetImmediate(false)
	} else {
		renewer.SetImmediate(true)
	}

	err = renewer.OneShot()
	if err != nil {
		renewer.Logf("renewing failed: %s", err)
		exit(1)
	}
}
<commit_msg>fix typo in log msg<commit_after>// Copyright © 2017 Pennock Tech, LLC.
// All rights reserved, except as granted under license.
// Licensed per file LICENSE.txt

package main // import "go.pennock.tech/ocsprenewer/cmd/ocsprenewer"

import (
	"flag"

	// AVOID IMPORTING "os" HERE: use util.go for that.

	"go.pennock.tech/ocsprenewer/renew"
)

const (
	defHTTPUserAgentProduct = "ocsprenewer"
	defHTTPUserAgentComment = "(Pennock Tech OCSP Renewer)"
)

// Should be overridden with a version number
const defHTTPUserAgent = defHTTPUserAgentProduct + " " + defHTTPUserAgentComment

// We don't use "daemon" because we don't auto-fork into background, but
// instead make ourselves easy to supervise.  If there are complaints that
// daemonization is too hard, we can consider log-file redirection and
// self-daemonization as later features.
var pflags struct {
	Persist   bool
	IfNeeded  bool
	Verbose   bool
	NotReally bool
	Version   bool
}

var renewerConfig renew.Config

func init() {
	flag.BoolVar(&pflags.Persist, "persist", false, "run in a loop, renewing as needed")
	flag.BoolVar(&pflags.Persist, "p", false, "short form of -persist")
	flag.BoolVar(&pflags.IfNeeded, "if-needed", false, "if not persisting, only renew those which need renewal per timers")
	flag.BoolVar(&pflags.Verbose, "verbose", false, "be more verbose")
	flag.BoolVar(&pflags.Verbose, "v", false, "short form of -verbose")
	flag.BoolVar(&pflags.NotReally, "not-really", false, "don't talk to remote servers, do everything else")
	flag.BoolVar(&pflags.NotReally, "n", false, "short form of -not-really")
	flag.BoolVar(&pflags.Version, "version", false, "show version and exit")

	flag.BoolVar(&renewerConfig.Immediate, "now", false, "renew immediately in persist mode")
	//TODO: flag.StringVar(&renewerConfig.HTTPStatus, "http", "", "start an HTTP status service, on given host:port spec")
	flag.BoolVar(&renewerConfig.Directories, "dirs", false, "arguments are directories containing certs")
	flag.StringVar(&renewerConfig.OutputDir, "out-dir", "./", "place files into given directory")
	flag.StringVar(&renewerConfig.Extension, "extension", ".ocsp", "create proofs in files with this extension")
	flag.Float64Var(&renewerConfig.TimerT1, "timer-t1", 0.5, "how far through staple validity period to start trying to renew")
	flag.BoolVar(&renewerConfig.AllowNonOCSPInDir, "allow-nonocsp-in-dir", false, "do not error on certs missing OCSP info")
	flag.StringVar(&renewerConfig.CertExtensions, "cert-extensions", ".crt .cert .pem", "files in dir-scan with these extensions should be certs")
}

func main() {
	flag.Parse()

	if pflags.Version {
		showVersion()
		exit(0)
	}

	renewerConfig.InputPaths = flag.Args()
	renewerConfig.HTTPUserAgent = defHTTPUserAgent

	if Version != "" {
		renewerConfig.HTTPUserAgent = defHTTPUserAgentProduct + "/" + httpVersion(Version) + " " + defHTTPUserAgentComment
	}

	renewer, err := renew.New(renewerConfig)
	if err != nil {
		stderr("configuring OCSP renewer failed: %s\n", err)
		exit(1)
	}
	if pflags.Verbose {
		renewer.SetLogLevel(1)
	}
	if pflags.NotReally {
		renewer.SetNotReally(true)
	}

	if err := renewer.BasicChecks(); err != nil {
		renewer.Logf("initial startup checks failed: %s", err)
		exit(1)
	}

	if pflags.Persist {
		renewer.Logf("%s: starting persistent run, version %s", ProjectName, Version)
		renewer.Logf("argv: %s", argvQuoted())
		setupSignals(renewer)
		// Should not return until exiting
		ok := renewer.Start()
		if ok {
			exit(0)
		}
		exit(1)
	}

	renewer.LogAtf(1, "%s: one-shot renewer run, version %s", ProjectName, Version)

	if pflags.IfNeeded {
		renewer.SetImmediate(false)
	} else {
		renewer.SetImmediate(true)
	}

	err = renewer.OneShot()
	if err != nil {
		renewer.Logf("renewing failed: %s", err)
		exit(1)
	}
}
<|endoftext|>


<commit_before>// +build debug

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"

	"github.com/spf13/cobra"

	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/pack"
	"github.com/restic/restic/internal/repository"
	"github.com/restic/restic/internal/restic"
)

var cmdDebug = &cobra.Command{
	Use:   "debug",
	Short: "Debug commands",
}

var cmdDebugDump = &cobra.Command{
	Use:   "dump [indexes|snapshots|all|packs]",
	Short: "Dump data structures",
	Long: `
The "dump" command dumps data structures from the repository as JSON objects. It
is used for debugging purposes only.

EXIT STATUS
===========

Exit status is 0 if the command was successful, and non-zero if there was any error.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runDebugDump(globalOptions, args)
	},
}

func init() {
	cmdRoot.AddCommand(cmdDebug)
	cmdDebug.AddCommand(cmdDebugDump)
}

func prettyPrintJSON(wr io.Writer, item interface{}) error {
	buf, err := json.MarshalIndent(item, "", "  ")
	if err != nil {
		return err
	}

	_, err = wr.Write(append(buf, '\n'))
	return err
}

func debugPrintSnapshots(repo *repository.Repository, wr io.Writer) error {
	return repo.List(context.TODO(), restic.SnapshotFile, func(id restic.ID, size int64) error {
		snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id)
		if err != nil {
			return err
		}

		fmt.Fprintf(wr, "snapshot_id: %v\n", id)

		return prettyPrintJSON(wr, snapshot)
	})
}

// Pack is the struct used in printPacks.
type Pack struct {
	Name string `json:"name"`

	Blobs []Blob `json:"blobs"`
}

// Blob is the struct used in printPacks.
type Blob struct {
	Type   restic.BlobType `json:"type"`
	Length uint            `json:"length"`
	ID     restic.ID       `json:"id"`
	Offset uint            `json:"offset"`
}

func printPacks(repo *repository.Repository, wr io.Writer) error {

	return repo.List(context.TODO(), restic.PackFile, func(id restic.ID, size int64) error {
		h := restic.Handle{Type: restic.PackFile, Name: id.String()}

		blobs, err := pack.List(repo.Key(), restic.ReaderAt(repo.Backend(), h), size)
		if err != nil {
			Warnf("error for pack %v: %v\n", id.Str(), err)
			return nil
		}

		p := Pack{
			Name:  id.String(),
			Blobs: make([]Blob, len(blobs)),
		}
		for i, blob := range blobs {
			p.Blobs[i] = Blob{
				Type:   blob.Type,
				Length: blob.Length,
				ID:     blob.ID,
				Offset: blob.Offset,
			}
		}

		return prettyPrintJSON(wr, p)
	})
}

func dumpIndexes(repo restic.Repository, wr io.Writer) error {
	return repo.List(context.TODO(), restic.IndexFile, func(id restic.ID, size int64) error {
		Printf("index_id: %v\n", id)

		idx, err := repository.LoadIndex(context.TODO(), repo, id)
		if err != nil {
			return err
		}

		return idx.Dump(wr)
	})
}

func runDebugDump(gopts GlobalOptions, args []string) error {
	if len(args) != 1 {
		return errors.Fatal("type not specified")
	}

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	tpe := args[0]

	switch tpe {
	case "indexes":
		return dumpIndexes(repo, gopts.stdout)
	case "snapshots":
		return debugPrintSnapshots(repo, gopts.stdout)
	case "packs":
		return printPacks(repo, gopts.stdout)
	case "all":
		Printf("snapshots:\n")
		err := debugPrintSnapshots(repo, gopts.stdout)
		if err != nil {
			return err
		}

		Printf("\nindexes:\n")
		err = dumpIndexes(repo, gopts.stdout)
		if err != nil {
			return err
		}

		return nil
	default:
		return errors.Fatalf("no such type %q", tpe)
	}
}
<commit_msg>debug: use proper context<commit_after>// +build debug

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"

	"github.com/spf13/cobra"

	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/pack"
	"github.com/restic/restic/internal/repository"
	"github.com/restic/restic/internal/restic"
)

var cmdDebug = &cobra.Command{
	Use:   "debug",
	Short: "Debug commands",
}

var cmdDebugDump = &cobra.Command{
	Use:   "dump [indexes|snapshots|all|packs]",
	Short: "Dump data structures",
	Long: `
The "dump" command dumps data structures from the repository as JSON objects. It
is used for debugging purposes only.

EXIT STATUS
===========

Exit status is 0 if the command was successful, and non-zero if there was any error.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runDebugDump(globalOptions, args)
	},
}

func init() {
	cmdRoot.AddCommand(cmdDebug)
	cmdDebug.AddCommand(cmdDebugDump)
}

func prettyPrintJSON(wr io.Writer, item interface{}) error {
	buf, err := json.MarshalIndent(item, "", "  ")
	if err != nil {
		return err
	}

	_, err = wr.Write(append(buf, '\n'))
	return err
}

func debugPrintSnapshots(ctx context.Context, repo *repository.Repository, wr io.Writer) error {
	return repo.List(ctx, restic.SnapshotFile, func(id restic.ID, size int64) error {
		snapshot, err := restic.LoadSnapshot(ctx, repo, id)
		if err != nil {
			return err
		}

		fmt.Fprintf(wr, "snapshot_id: %v\n", id)

		return prettyPrintJSON(wr, snapshot)
	})
}

// Pack is the struct used in printPacks.
type Pack struct {
	Name string `json:"name"`

	Blobs []Blob `json:"blobs"`
}

// Blob is the struct used in printPacks.
type Blob struct {
	Type   restic.BlobType `json:"type"`
	Length uint            `json:"length"`
	ID     restic.ID       `json:"id"`
	Offset uint            `json:"offset"`
}

func printPacks(ctx context.Context, repo *repository.Repository, wr io.Writer) error {

	return repo.List(ctx, restic.PackFile, func(id restic.ID, size int64) error {
		h := restic.Handle{Type: restic.PackFile, Name: id.String()}

		blobs, err := pack.List(repo.Key(), restic.ReaderAt(repo.Backend(), h), size)
		if err != nil {
			Warnf("error for pack %v: %v\n", id.Str(), err)
			return nil
		}

		p := Pack{
			Name:  id.String(),
			Blobs: make([]Blob, len(blobs)),
		}
		for i, blob := range blobs {
			p.Blobs[i] = Blob{
				Type:   blob.Type,
				Length: blob.Length,
				ID:     blob.ID,
				Offset: blob.Offset,
			}
		}

		return prettyPrintJSON(wr, p)
	})
}

func dumpIndexes(ctx context.Context, repo restic.Repository, wr io.Writer) error {
	return repo.List(ctx, restic.IndexFile, func(id restic.ID, size int64) error {
		Printf("index_id: %v\n", id)

		idx, err := repository.LoadIndex(ctx, repo, id)
		if err != nil {
			return err
		}

		return idx.Dump(wr)
	})
}

func runDebugDump(gopts GlobalOptions, args []string) error {
	if len(args) != 1 {
		return errors.Fatal("type not specified")
	}

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	tpe := args[0]

	switch tpe {
	case "indexes":
		return dumpIndexes(gopts.ctx, repo, gopts.stdout)
	case "snapshots":
		return debugPrintSnapshots(gopts.ctx, repo, gopts.stdout)
	case "packs":
		return printPacks(gopts.ctx, repo, gopts.stdout)
	case "all":
		Printf("snapshots:\n")
		err := debugPrintSnapshots(gopts.ctx, repo, gopts.stdout)
		if err != nil {
			return err
		}

		Printf("\nindexes:\n")
		err = dumpIndexes(gopts.ctx, repo, gopts.stdout)
		if err != nil {
			return err
		}

		return nil
	default:
		return errors.Fatalf("no such type %q", tpe)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold 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 sources

import (
	"context"
	"fmt"
	"io"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
)

// BuildContextSource is the generic type for the different build context sources the kaniko builder can use
type BuildContextSource interface {
	Setup(ctx context.Context, out io.Writer, artifact *latest.Artifact, initialTag string, dependencies []string) (string, error)
	Pod(args []string) *v1.Pod
	ModifyPod(ctx context.Context, p *v1.Pod) error
	Cleanup(ctx context.Context) error
}

// Retrieve returns the correct build context based on the config
func Retrieve(cli *kubectl.CLI, clusterDetails *latest.ClusterDetails, artifact *latest.KanikoArtifact) BuildContextSource {
	if artifact.BuildContext.LocalDir != nil {
		return &LocalDir{
			clusterDetails: clusterDetails,
			artifact:       artifact,
			kubectl:        cli,
		}
	}

	return &GCSBucket{
		clusterDetails: clusterDetails,
		artifact:       artifact,
	}
}

func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoArtifact, args []string, version string) *v1.Pod {
	userAgent := fmt.Sprintf("UpstreamClient(skaffold-%s)", version)

	env := []v1.EnvVar{{
		Name:  "GOOGLE_APPLICATION_CREDENTIALS",
		Value: "/secret/kaniko-secret",
	}, {
		// This should be same https://github.com/GoogleContainerTools/kaniko/blob/77cfb912f3483c204bfd09e1ada44fd200b15a78/pkg/executor/push.go#L49
		Name:  "UPSTREAM_CLIENT_TYPE",
		Value: userAgent,
	}}

	env = setProxy(clusterDetails, env)

	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "kaniko-",
			Labels:       map[string]string{"skaffold-kaniko": "skaffold-kaniko"},
			Namespace:    clusterDetails.Namespace,
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:            constants.DefaultKanikoContainerName,
					Image:           artifact.Image,
					Args:            args,
					ImagePullPolicy: v1.PullIfNotPresent,
					Env:             env,
					Resources:       resourceRequirements(clusterDetails.Resources),
				},
			},
			RestartPolicy: v1.RestartPolicyNever,
		},
	}

	// Add secret for pull secret
	if clusterDetails.PullSecretName != "" {
		addSecretVolume(pod, constants.DefaultKanikoSecretName, "/secret", clusterDetails.PullSecretName)
	}

	// Add host path volume for cache
	if artifact.Cache != nil && artifact.Cache.HostPath != "" {
	    addHostPathVolume(pod, constants.DefaultKanikoCacheDirName, constants.DefaultKanikoCacheDirMountPath, artifact.Cache.HostPath)
	}

	if clusterDetails.DockerConfig == nil {
		return pod
	}

	// Add secret for docker config if specified
	addSecretVolume(pod, constants.DefaultKanikoDockerConfigSecretName, constants.DefaultKanikoDockerConfigPath, clusterDetails.DockerConfig.SecretName )
	return pod
}

func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string) {
	vm := v1.VolumeMount{
		Name:      name,
		MountPath: mountPath,
	}
	v := v1.Volume{
		Name: name,
		VolumeSource: v1.VolumeSource{
			Secret: &v1.SecretVolumeSource{
				SecretName: secretName,
			},
		},
	}
	pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, vm)
	pod.Spec.Volumes = append(pod.Spec.Volumes, v)
}

func addHostPathVolume(pod *v1.Pod, name, mountPath, path string) {
	vm := v1.VolumeMount{
		Name:      name,
		MountPath: mountPath,
	}
	v := v1.Volume{
		Name: name,
		VolumeSource: v1.VolumeSource{
			HostPath: &v1.HostPathVolumeSource{
				Path: path,
			},
		},
	}
	pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, vm)
	pod.Spec.Volumes = append(pod.Spec.Volumes, v)
}

func setProxy(clusterDetails *latest.ClusterDetails, env []v1.EnvVar) []v1.EnvVar {
	if clusterDetails.HTTPProxy != "" {
		proxy := v1.EnvVar{
			Name:  "HTTP_PROXY",
			Value: clusterDetails.HTTPProxy,
		}
		env = append(env, proxy)
	}

	if clusterDetails.HTTPSProxy != "" {
		proxy := v1.EnvVar{
			Name:  "HTTPS_PROXY",
			Value: clusterDetails.HTTPSProxy,
		}
		env = append(env, proxy)
	}
	return env
}

func resourceRequirements(rr *latest.ResourceRequirements) v1.ResourceRequirements {
	req := v1.ResourceRequirements{}

	if rr != nil {
		if rr.Limits != nil {
			req.Limits = v1.ResourceList{}
			if rr.Limits.CPU != "" {
				req.Limits[v1.ResourceCPU] = resource.MustParse(rr.Limits.CPU)
			}

			if rr.Limits.Memory != "" {
				req.Limits[v1.ResourceMemory] = resource.MustParse(rr.Limits.Memory)
			}
		}

		if rr.Requests != nil {
			req.Requests = v1.ResourceList{}
			if rr.Requests.CPU != "" {
				req.Requests[v1.ResourceCPU] = resource.MustParse(rr.Requests.CPU)
			}
			if rr.Requests.Memory != "" {
				req.Requests[v1.ResourceMemory] = resource.MustParse(rr.Requests.Memory)
			}
		}
	}

	return req
}
<commit_msg>fixing lint<commit_after>/*
Copyright 2019 The Skaffold 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 sources

import (
	"context"
	"fmt"
	"io"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubectl"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
)

// BuildContextSource is the generic type for the different build context sources the kaniko builder can use
type BuildContextSource interface {
	Setup(ctx context.Context, out io.Writer, artifact *latest.Artifact, initialTag string, dependencies []string) (string, error)
	Pod(args []string) *v1.Pod
	ModifyPod(ctx context.Context, p *v1.Pod) error
	Cleanup(ctx context.Context) error
}

// Retrieve returns the correct build context based on the config
func Retrieve(cli *kubectl.CLI, clusterDetails *latest.ClusterDetails, artifact *latest.KanikoArtifact) BuildContextSource {
	if artifact.BuildContext.LocalDir != nil {
		return &LocalDir{
			clusterDetails: clusterDetails,
			artifact:       artifact,
			kubectl:        cli,
		}
	}

	return &GCSBucket{
		clusterDetails: clusterDetails,
		artifact:       artifact,
	}
}

func podTemplate(clusterDetails *latest.ClusterDetails, artifact *latest.KanikoArtifact, args []string, version string) *v1.Pod {
	userAgent := fmt.Sprintf("UpstreamClient(skaffold-%s)", version)

	env := []v1.EnvVar{{
		Name:  "GOOGLE_APPLICATION_CREDENTIALS",
		Value: "/secret/kaniko-secret",
	}, {
		// This should be same https://github.com/GoogleContainerTools/kaniko/blob/77cfb912f3483c204bfd09e1ada44fd200b15a78/pkg/executor/push.go#L49
		Name:  "UPSTREAM_CLIENT_TYPE",
		Value: userAgent,
	}}

	env = setProxy(clusterDetails, env)

	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			GenerateName: "kaniko-",
			Labels:       map[string]string{"skaffold-kaniko": "skaffold-kaniko"},
			Namespace:    clusterDetails.Namespace,
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:            constants.DefaultKanikoContainerName,
					Image:           artifact.Image,
					Args:            args,
					ImagePullPolicy: v1.PullIfNotPresent,
					Env:             env,
					Resources:       resourceRequirements(clusterDetails.Resources),
				},
			},
			RestartPolicy: v1.RestartPolicyNever,
		},
	}

	// Add secret for pull secret
	if clusterDetails.PullSecretName != "" {
		addSecretVolume(pod, constants.DefaultKanikoSecretName, "/secret", clusterDetails.PullSecretName)
	}

	// Add host path volume for cache
	if artifact.Cache != nil && artifact.Cache.HostPath != "" {
		addHostPathVolume(pod, constants.DefaultKanikoCacheDirName, constants.DefaultKanikoCacheDirMountPath, artifact.Cache.HostPath)
	}

	if clusterDetails.DockerConfig == nil {
		return pod
	}

	// Add secret for docker config if specified
	addSecretVolume(pod, constants.DefaultKanikoDockerConfigSecretName, constants.DefaultKanikoDockerConfigPath, clusterDetails.DockerConfig.SecretName)
	return pod
}

func addSecretVolume(pod *v1.Pod, name, mountPath, secretName string) {
	vm := v1.VolumeMount{
		Name:      name,
		MountPath: mountPath,
	}
	v := v1.Volume{
		Name: name,
		VolumeSource: v1.VolumeSource{
			Secret: &v1.SecretVolumeSource{
				SecretName: secretName,
			},
		},
	}
	pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, vm)
	pod.Spec.Volumes = append(pod.Spec.Volumes, v)
}

func addHostPathVolume(pod *v1.Pod, name, mountPath, path string) {
	vm := v1.VolumeMount{
		Name:      name,
		MountPath: mountPath,
	}
	v := v1.Volume{
		Name: name,
		VolumeSource: v1.VolumeSource{
			HostPath: &v1.HostPathVolumeSource{
				Path: path,
			},
		},
	}
	pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, vm)
	pod.Spec.Volumes = append(pod.Spec.Volumes, v)
}

func setProxy(clusterDetails *latest.ClusterDetails, env []v1.EnvVar) []v1.EnvVar {
	if clusterDetails.HTTPProxy != "" {
		proxy := v1.EnvVar{
			Name:  "HTTP_PROXY",
			Value: clusterDetails.HTTPProxy,
		}
		env = append(env, proxy)
	}

	if clusterDetails.HTTPSProxy != "" {
		proxy := v1.EnvVar{
			Name:  "HTTPS_PROXY",
			Value: clusterDetails.HTTPSProxy,
		}
		env = append(env, proxy)
	}
	return env
}

func resourceRequirements(rr *latest.ResourceRequirements) v1.ResourceRequirements {
	req := v1.ResourceRequirements{}

	if rr != nil {
		if rr.Limits != nil {
			req.Limits = v1.ResourceList{}
			if rr.Limits.CPU != "" {
				req.Limits[v1.ResourceCPU] = resource.MustParse(rr.Limits.CPU)
			}

			if rr.Limits.Memory != "" {
				req.Limits[v1.ResourceMemory] = resource.MustParse(rr.Limits.Memory)
			}
		}

		if rr.Requests != nil {
			req.Requests = v1.ResourceList{}
			if rr.Requests.CPU != "" {
				req.Requests[v1.ResourceCPU] = resource.MustParse(rr.Requests.CPU)
			}
			if rr.Requests.Memory != "" {
				req.Requests[v1.ResourceMemory] = resource.MustParse(rr.Requests.Memory)
			}
		}
	}

	return req
}
<|endoftext|>