<commit_before>// Copyright 2016 The Cockroach 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.
//
// Author: Nathan VanBenschoten (nvanbenschoten@gmail.com)

package sql

import (
	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach/sql/parser"
)

// PreparedStatement is a SQL statement that has been parsed and the types
// of arguments and results have been determined.
type PreparedStatement struct {
	Query       string
	SQLTypes    parser.PlaceholderTypes
	Columns     []ResultColumn
	portalNames map[string]struct{}

	ProtocolMeta interface{} // a field for protocol implementations to hang metadata off of.
}

// PreparedStatements is a mapping of PreparedStatement names to their
// corresponding PreparedStatements.
type PreparedStatements struct {
	session *Session
	stmts   map[string]*PreparedStatement
}

func makePreparedStatements(s *Session) PreparedStatements {
	return PreparedStatements{
		session: s,
		stmts:   make(map[string]*PreparedStatement),
	}
}

// Get returns the PreparedStatement with the provided name.
func (ps PreparedStatements) Get(name string) (*PreparedStatement, bool) {
	stmt, ok := ps.stmts[name]
	return stmt, ok
}

// Exists returns whether a PreparedStatement with the provided name exists.
func (ps PreparedStatements) Exists(name string) bool {
	_, ok := ps.Get(name)
	return ok
}

// New creates a new PreparedStatement with the provided name and corresponding
// query string, using the given PlaceholderTypes hints to assist in inferring
// placeholder types.
func (ps PreparedStatements) New(
	ctx context.Context,
	e *Executor,
	name, query string,
	placeholderHints parser.PlaceholderTypes,
) (*PreparedStatement, error) {
	// Prepare the query. This completes the typing of placeholders.
	cols, err := e.Prepare(ctx, query, ps.session, placeholderHints)
	if err != nil {
		return nil, err
	}
	stmt := &PreparedStatement{
		Query:       query,
		SQLTypes:    placeholderHints,
		Columns:     cols,
		portalNames: make(map[string]struct{}),
	}
	ps.stmts[name] = stmt
	return stmt, nil
}

// Delete removes the PreparedStatements with the provided name from the PreparedStatements.
func (ps PreparedStatements) Delete(name string) {
	if stmt, ok := ps.Get(name); ok {
		for portalName := range stmt.portalNames {
			delete(ps.session.PreparedPortals.portals, portalName)
		}
		delete(ps.stmts, name)
	}
}

// PreparedPortal is a PreparedStatement that has been bound with query arguments.
type PreparedPortal struct {
	Stmt  *PreparedStatement
	Qargs parser.QueryArguments

	ProtocolMeta interface{} // a field for protocol implementations to hang metadata off of.
}

// PreparedPortals is a mapping of PreparedPortal names to their corresponding
// PreparedPortals.
type PreparedPortals struct {
	session *Session
	portals map[string]*PreparedPortal
}

func makePreparedPortals(s *Session) PreparedPortals {
	return PreparedPortals{
		session: s,
		portals: make(map[string]*PreparedPortal),
	}
}

// Get returns the PreparedPortal with the provided name.
func (pp PreparedPortals) Get(name string) (*PreparedPortal, bool) {
	portal, ok := pp.portals[name]
	return portal, ok
}

// Exists returns whether a PreparedPortal with the provided name exists.
func (pp PreparedPortals) Exists(name string) bool {
	_, ok := pp.Get(name)
	return ok
}

// New creates a new PreparedPortal with the provided name and corresponding
// PreparedStatement, binding the statement using the given QueryArguments.
func (pp PreparedPortals) New(name string, stmt *PreparedStatement, qargs parser.QueryArguments,
) *PreparedPortal {
	stmt.portalNames[name] = struct{}{}
	portal := &PreparedPortal{
		Stmt:  stmt,
		Qargs: qargs,
	}
	pp.portals[name] = portal
	return portal
}

// Delete removes the PreparedPortal with the provided name from the PreparedPortals.
func (pp PreparedPortals) Delete(name string) {
	if portal, ok := pp.Get(name); ok {
		delete(portal.Stmt.portalNames, name)
		delete(pp.portals, name)
	}
}
<commit_msg>sql: Add PreparedStatements.DeleteAll method<commit_after>// Copyright 2016 The Cockroach 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.
//
// Author: Nathan VanBenschoten (nvanbenschoten@gmail.com)

package sql

import (
	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach/sql/parser"
)

// PreparedStatement is a SQL statement that has been parsed and the types
// of arguments and results have been determined.
type PreparedStatement struct {
	Query       string
	SQLTypes    parser.PlaceholderTypes
	Columns     []ResultColumn
	portalNames map[string]struct{}

	ProtocolMeta interface{} // a field for protocol implementations to hang metadata off of.
}

// PreparedStatements is a mapping of PreparedStatement names to their
// corresponding PreparedStatements.
type PreparedStatements struct {
	session *Session
	stmts   map[string]*PreparedStatement
}

func makePreparedStatements(s *Session) PreparedStatements {
	return PreparedStatements{
		session: s,
		stmts:   make(map[string]*PreparedStatement),
	}
}

// Get returns the PreparedStatement with the provided name.
func (ps PreparedStatements) Get(name string) (*PreparedStatement, bool) {
	stmt, ok := ps.stmts[name]
	return stmt, ok
}

// Exists returns whether a PreparedStatement with the provided name exists.
func (ps PreparedStatements) Exists(name string) bool {
	_, ok := ps.Get(name)
	return ok
}

// New creates a new PreparedStatement with the provided name and corresponding
// query string, using the given PlaceholderTypes hints to assist in inferring
// placeholder types.
func (ps PreparedStatements) New(
	ctx context.Context,
	e *Executor,
	name, query string,
	placeholderHints parser.PlaceholderTypes,
) (*PreparedStatement, error) {
	// Prepare the query. This completes the typing of placeholders.
	cols, err := e.Prepare(ctx, query, ps.session, placeholderHints)
	if err != nil {
		return nil, err
	}
	stmt := &PreparedStatement{
		Query:       query,
		SQLTypes:    placeholderHints,
		Columns:     cols,
		portalNames: make(map[string]struct{}),
	}
	ps.stmts[name] = stmt
	return stmt, nil
}

// Delete removes the PreparedStatement with the provided name from the PreparedStatements.
// The method returns whether a statement with that name was found and removed.
func (ps PreparedStatements) Delete(name string) bool {
	if stmt, ok := ps.Get(name); ok {
		for portalName := range stmt.portalNames {
			delete(ps.session.PreparedPortals.portals, portalName)
		}
		delete(ps.stmts, name)
		return true
	}
	return false
}

// DeleteAll removes all PreparedStatements from the PreparedStatements. This will in turn
// remove all PreparedPortals from the session's PreparedPortals.
func (ps PreparedStatements) DeleteAll() {
	ps.stmts = make(map[string]*PreparedStatement)
	ps.session.PreparedPortals.portals = make(map[string]*PreparedPortal)
}

// PreparedPortal is a PreparedStatement that has been bound with query arguments.
type PreparedPortal struct {
	Stmt  *PreparedStatement
	Qargs parser.QueryArguments

	ProtocolMeta interface{} // a field for protocol implementations to hang metadata off of.
}

// PreparedPortals is a mapping of PreparedPortal names to their corresponding
// PreparedPortals.
type PreparedPortals struct {
	session *Session
	portals map[string]*PreparedPortal
}

func makePreparedPortals(s *Session) PreparedPortals {
	return PreparedPortals{
		session: s,
		portals: make(map[string]*PreparedPortal),
	}
}

// Get returns the PreparedPortal with the provided name.
func (pp PreparedPortals) Get(name string) (*PreparedPortal, bool) {
	portal, ok := pp.portals[name]
	return portal, ok
}

// Exists returns whether a PreparedPortal with the provided name exists.
func (pp PreparedPortals) Exists(name string) bool {
	_, ok := pp.Get(name)
	return ok
}

// New creates a new PreparedPortal with the provided name and corresponding
// PreparedStatement, binding the statement using the given QueryArguments.
func (pp PreparedPortals) New(name string, stmt *PreparedStatement, qargs parser.QueryArguments,
) *PreparedPortal {
	stmt.portalNames[name] = struct{}{}
	portal := &PreparedPortal{
		Stmt:  stmt,
		Qargs: qargs,
	}
	pp.portals[name] = portal
	return portal
}

// Delete removes the PreparedPortal with the provided name from the PreparedPortals.
// The method returns whether a portal with that name was found and removed.
func (pp PreparedPortals) Delete(name string) bool {
	if portal, ok := pp.Get(name); ok {
		delete(portal.Stmt.portalNames, name)
		delete(pp.portals, name)
		return true
	}
	return false
}
<|endoftext|>


<commit_before>/*
Copyright 2018 Google, Inc. 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 convert

import (
	"encoding/json"
	"reflect"

	"google.golang.org/api/cloudbuild/v1"
	corev1 "k8s.io/api/core/v1"

	v1alpha1 "github.com/google/build-crd/pkg/apis/cloudbuild/v1alpha1"
	"github.com/google/build-crd/pkg/builder/validation"
)

const (
	customSource = "custom-source"
)

var (
	emptyVolumeSource = corev1.VolumeSource{
		EmptyDir: &corev1.EmptyDirVolumeSource{},
	}
	socketType               = corev1.HostPathSocket
	daemonSocketVolumeSource = corev1.VolumeSource{
		HostPath: &corev1.HostPathVolumeSource{
			Path: "/var/run/docker.sock",
			Type: &socketType,
		},
	}
)

func remarshal(in, out interface{}) error {
	bts, err := json.Marshal(in)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(bts, &out); err != nil {
		return err
	}
	return nil
}

func FromCRD(u *v1alpha1.BuildSpec) (*cloudbuild.Build, error) {
	if u.ServiceAccountName != "" {
		return nil, fmt.Errorf("Unsupported: ServiceAccountName with Google builder, got: %v", u.ServiceAccountName)
	}
	bld := cloudbuild.Build{
		Steps: make([]*cloudbuild.BuildStep, 0, len(u.Steps)),
	}
	if u.Source != nil {
		switch {
		case u.Source.Git != nil:
			scm, err := ToRepoSourceFromGit(u.Source.Git)
			if err != nil {
				return nil, err
			}
			bld.Source = &cloudbuild.Source{
				RepoSource: scm,
			}

		case u.Source.Custom != nil:
			step, err := ToStepFromContainer(u.Source.Custom)
			if err != nil {
				return nil, err
			}
			step.Id = customSource
			bld.Steps = append(bld.Steps, step)

		default:
			return nil, validation.NewError("UnsupportedSource", "unsupported Source, got %v", u.Source)
		}
	}
	// We only support roundtripping emptyDir volumes, but we support hostPath volumes
	// of the daemon socket as input, which GCB provides to all steps whether they need
	// it or not.
	socketVolume := ""
	for _, v := range u.Volumes {
		switch {
		case reflect.DeepEqual(v.VolumeSource, daemonSocketVolumeSource):
			// This is a lossy translation, we cannot roundtrip this without
			// upgrading every ToCRD to a privileged Build.
			socketVolume = v.Name
		case reflect.DeepEqual(v.VolumeSource, emptyVolumeSource):
		default:
			return nil, validation.NewError("UnsupportedVolume", "only certain volumes are supported on the Google builder, got %v", v.VolumeSource)
		}
	}
	for _, c := range u.Steps {
		step, err := ToStepFromContainer(&c)
		if err != nil {
			return nil, err
		}
		var vms []*cloudbuild.Volume
		for _, vm := range step.Volumes {
			if vm.Name == socketVolume {
				continue
			}
			vms = append(vms, vm)
		}
		step.Volumes = vms
		bld.Steps = append(bld.Steps, step)
	}
	return &bld, nil
}

func ToCRD(u *cloudbuild.Build) (*v1alpha1.BuildSpec, error) {
	bld := v1alpha1.BuildSpec{
		Steps: make([]corev1.Container, 0, len(u.Steps)),
	}
	steps := u.Steps
	switch {
	case u.Source != nil && u.Source.RepoSource != nil:
		scm, err := ToGitFromRepoSource(u.Source.RepoSource)
		if err != nil {
			return nil, err
		}
		bld.Source = &v1alpha1.SourceSpec{
			Git: scm,
		}
	case steps[0].Id == customSource:
		c, err := ToContainerFromStep(steps[0])
		if err != nil {
			return nil, err
		}
		c.Name = ""
		steps = steps[1:]
		bld.Source = &v1alpha1.SourceSpec{
			Custom: c,
		}
	case u.Source != nil:
		return nil, validation.NewError("UnsupportedSource", "unsupported Source, got: %v", u.Source)
	}
	volumeNames := make(map[string]bool)
	for _, step := range steps {
		c, err := ToContainerFromStep(step)
		if err != nil {
			return nil, err
		}
		for _, v := range c.VolumeMounts {
			volumeNames[v.Name] = true
		}
		bld.Steps = append(bld.Steps, *c)
	}

	// Create emptyDir volume entries, which is all GCB supports.
	for k, _ := range volumeNames {
		bld.Volumes = append(bld.Volumes, corev1.Volume{
			Name:         k,
			VolumeSource: emptyVolumeSource,
		})
	}

	return &bld, nil
}
<commit_msg>Fix build breakage (#34)<commit_after>/*
Copyright 2018 Google, Inc. 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 convert

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

	"google.golang.org/api/cloudbuild/v1"
	corev1 "k8s.io/api/core/v1"

	v1alpha1 "github.com/google/build-crd/pkg/apis/cloudbuild/v1alpha1"
	"github.com/google/build-crd/pkg/builder/validation"
)

const (
	customSource = "custom-source"
)

var (
	emptyVolumeSource = corev1.VolumeSource{
		EmptyDir: &corev1.EmptyDirVolumeSource{},
	}
	socketType               = corev1.HostPathSocket
	daemonSocketVolumeSource = corev1.VolumeSource{
		HostPath: &corev1.HostPathVolumeSource{
			Path: "/var/run/docker.sock",
			Type: &socketType,
		},
	}
)

func remarshal(in, out interface{}) error {
	bts, err := json.Marshal(in)
	if err != nil {
		return err
	}
	if err := json.Unmarshal(bts, &out); err != nil {
		return err
	}
	return nil
}

func FromCRD(u *v1alpha1.BuildSpec) (*cloudbuild.Build, error) {
	if u.ServiceAccountName != "" {
		return nil, fmt.Errorf("Unsupported: ServiceAccountName with Google builder, got: %v", u.ServiceAccountName)
	}
	bld := cloudbuild.Build{
		Steps: make([]*cloudbuild.BuildStep, 0, len(u.Steps)),
	}
	if u.Source != nil {
		switch {
		case u.Source.Git != nil:
			scm, err := ToRepoSourceFromGit(u.Source.Git)
			if err != nil {
				return nil, err
			}
			bld.Source = &cloudbuild.Source{
				RepoSource: scm,
			}

		case u.Source.Custom != nil:
			step, err := ToStepFromContainer(u.Source.Custom)
			if err != nil {
				return nil, err
			}
			step.Id = customSource
			bld.Steps = append(bld.Steps, step)

		default:
			return nil, validation.NewError("UnsupportedSource", "unsupported Source, got %v", u.Source)
		}
	}
	// We only support roundtripping emptyDir volumes, but we support hostPath volumes
	// of the daemon socket as input, which GCB provides to all steps whether they need
	// it or not.
	socketVolume := ""
	for _, v := range u.Volumes {
		switch {
		case reflect.DeepEqual(v.VolumeSource, daemonSocketVolumeSource):
			// This is a lossy translation, we cannot roundtrip this without
			// upgrading every ToCRD to a privileged Build.
			socketVolume = v.Name
		case reflect.DeepEqual(v.VolumeSource, emptyVolumeSource):
		default:
			return nil, validation.NewError("UnsupportedVolume", "only certain volumes are supported on the Google builder, got %v", v.VolumeSource)
		}
	}
	for _, c := range u.Steps {
		step, err := ToStepFromContainer(&c)
		if err != nil {
			return nil, err
		}
		var vms []*cloudbuild.Volume
		for _, vm := range step.Volumes {
			if vm.Name == socketVolume {
				continue
			}
			vms = append(vms, vm)
		}
		step.Volumes = vms
		bld.Steps = append(bld.Steps, step)
	}
	return &bld, nil
}

func ToCRD(u *cloudbuild.Build) (*v1alpha1.BuildSpec, error) {
	bld := v1alpha1.BuildSpec{
		Steps: make([]corev1.Container, 0, len(u.Steps)),
	}
	steps := u.Steps
	switch {
	case u.Source != nil && u.Source.RepoSource != nil:
		scm, err := ToGitFromRepoSource(u.Source.RepoSource)
		if err != nil {
			return nil, err
		}
		bld.Source = &v1alpha1.SourceSpec{
			Git: scm,
		}
	case steps[0].Id == customSource:
		c, err := ToContainerFromStep(steps[0])
		if err != nil {
			return nil, err
		}
		c.Name = ""
		steps = steps[1:]
		bld.Source = &v1alpha1.SourceSpec{
			Custom: c,
		}
	case u.Source != nil:
		return nil, validation.NewError("UnsupportedSource", "unsupported Source, got: %v", u.Source)
	}
	volumeNames := make(map[string]bool)
	for _, step := range steps {
		c, err := ToContainerFromStep(step)
		if err != nil {
			return nil, err
		}
		for _, v := range c.VolumeMounts {
			volumeNames[v.Name] = true
		}
		bld.Steps = append(bld.Steps, *c)
	}

	// Create emptyDir volume entries, which is all GCB supports.
	for k, _ := range volumeNames {
		bld.Volumes = append(bld.Volumes, corev1.Volume{
			Name:         k,
			VolumeSource: emptyVolumeSource,
		})
	}

	return &bld, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2022 The Tekton 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 git

const (
	// defaultTimeoutKey is the configuration field name for controlling
	// the maximum duration of a resolution request for a file from git.
	defaultTimeoutKey = "fetch-timeout"

	// defaultURLKey is the configuration field name for controlling
	// the git url to fetch the remote resource from.
	defaultURLKey = "default-url"

	// defaultRevisionKey is the configuration field name for controlling
	// the revision to fetch the remote resource from.
	defaultRevisionKey = "default-revision"

	// defaultOrgKey is the configuration field name for setting a default organization when using the SCM API.
	defaultOrgKey = "default-org"

	// ServerURLKey is the config map key for the SCM provider URL
	ServerURLKey = "server-url"
	// SCMTypeKey is the config map key for the SCM provider type
	SCMTypeKey = "scm-type"
	// APISecretNameKey is the config map key for the token secret's name
	APISecretNameKey = "api-token-secret-name"
	// APISecretKeyKey is the config map key for the containing the token within the token secret
	APISecretKeyKey = "api-token-secret-key"
	// APISecretNamespaceKey is the config map key for the token secret's namespace
	APISecretNamespaceKey = "api-token-namespace"
)
<commit_msg>Fix the value for `APISecretNamespaceKey`<commit_after>/*
Copyright 2022 The Tekton 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 git

const (
	// defaultTimeoutKey is the configuration field name for controlling
	// the maximum duration of a resolution request for a file from git.
	defaultTimeoutKey = "fetch-timeout"

	// defaultURLKey is the configuration field name for controlling
	// the git url to fetch the remote resource from.
	defaultURLKey = "default-url"

	// defaultRevisionKey is the configuration field name for controlling
	// the revision to fetch the remote resource from.
	defaultRevisionKey = "default-revision"

	// defaultOrgKey is the configuration field name for setting a default organization when using the SCM API.
	defaultOrgKey = "default-org"

	// ServerURLKey is the config map key for the SCM provider URL
	ServerURLKey = "server-url"
	// SCMTypeKey is the config map key for the SCM provider type
	SCMTypeKey = "scm-type"
	// APISecretNameKey is the config map key for the token secret's name
	APISecretNameKey = "api-token-secret-name"
	// APISecretKeyKey is the config map key for the containing the token within the token secret
	APISecretKeyKey = "api-token-secret-key"
	// APISecretNamespaceKey is the config map key for the token secret's namespace
	APISecretNamespaceKey = "api-token-secret-namespace"
)
<|endoftext|>


<commit_before>package pki

import (
	"fmt"
	"strings"
	"time"

	"github.com/fatih/structs"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func pathListRoles(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/?$",

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ListOperation: b.pathRoleList,
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func pathRoles(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/" + framework.GenericNameRegex("name"),
		Fields: map[string]*framework.FieldSchema{
			"name": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Name of the role",
			},

			"ttl": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "",
				Description: `The lease duration if no specific lease duration is
requested. The lease duration controls the expiration
of certificates issued by this backend. Defaults to
the value of max_ttl.`,
			},

			"max_ttl": &framework.FieldSchema{
				Type:        framework.TypeString,
				Default:     "",
				Description: "The maximum allowed lease duration",
			},

			"allow_localhost": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `Whether to allow "localhost" as a valid common
name in a request`,
			},

			"allowed_domains": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "",
				Description: `If set, clients can request certificates for
subdomains directly beneath these domains, including
the wildcard subdomains. See the documentation for more
information. This parameter accepts a comma-separated list
of domains.`,
			},

			"allow_bare_domains": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, clients can request certificates
for the base domains themselves, e.g. "example.com".
This is a separate option as in some cases this can
be considered a security threat.`,
			},

			"allow_subdomains": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, clients can request certificates for
subdomains of the CNs allowed by the other role options,
including wildcard subdomains. See the documentation for
more information.`,
			},

			"allow_any_name": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, clients can request certificates for
any CN they like. See the documentation for more
information.`,
			},

			"enforce_hostnames": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, only valid host names are allowed for
CN and SANs. Defaults to true.`,
			},

			"allow_ip_sans": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, IP Subject Alternative Names are allowed.
Any valid IP is accepted.`,
			},

			"server_flag": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, certificates are flagged for server auth use.
Defaults to true.`,
			},

			"client_flag": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, certificates are flagged for client auth use.
Defaults to true.`,
			},

			"code_signing_flag": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, certificates are flagged for code signing
use. Defaults to false.`,
			},

			"email_protection_flag": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, certificates are flagged for email
protection use. Defaults to false.`,
			},

			"key_type": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "rsa",
				Description: `The type of key to use; defaults to RSA. "rsa"
and "ec" are the only valid values.`,
			},

			"key_bits": &framework.FieldSchema{
				Type:    framework.TypeInt,
				Default: 2048,
				Description: `The number of bits to use. You will almost
certainly want to change this if you adjust
the key_type.`,
			},

			"use_csr_common_name": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, when used with a signing profile,
the common name in the CSR will be used. This
does *not* include any requested Subject Alternative
Names. Defaults to true.`,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ReadOperation:   b.pathRoleRead,
			logical.UpdateOperation: b.pathRoleCreate,
			logical.DeleteOperation: b.pathRoleDelete,
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func (b *backend) getRole(s logical.Storage, n string) (*roleEntry, error) {
	entry, err := s.Get("role/" + n)
	if err != nil {
		return nil, err
	}
	if entry == nil {
		return nil, nil
	}

	var result roleEntry
	if err := entry.DecodeJSON(&result); err != nil {
		return nil, err
	}

	// Migrate existing saved entries and save back if changed
	modified := false
	if len(result.TTL) == 0 && len(result.Lease) != 0 {
		result.TTL = result.Lease
		result.Lease = ""
		modified = true
	}
	if len(result.MaxTTL) == 0 && len(result.LeaseMax) != 0 {
		result.MaxTTL = result.LeaseMax
		result.LeaseMax = ""
		modified = true
	}
	if result.AllowBaseDomain {
		result.AllowBaseDomain = false
		result.AllowBareDomains = true
		modified = true
	}
	if result.AllowedBaseDomain != "" {
		found := false
		allowedDomains := strings.Split(result.AllowedDomains, ",")
		if len(allowedDomains) != 0 {
			for _, v := range allowedDomains {
				if v == result.AllowedBaseDomain {
					found = true
					break
				}
			}
		}
		if !found {
			if result.AllowedDomains == "" {
				result.AllowedDomains = result.AllowedBaseDomain
			} else {
				result.AllowedDomains += "," + result.AllowedBaseDomain
			}
		}
		result.AllowedBaseDomain = ""
		modified = true
	}

	if modified {
		jsonEntry, err := logical.StorageEntryJSON("role/"+n, &result)
		if err != nil {
			return nil, err
		}
		if err := s.Put(jsonEntry); err != nil {
			return nil, err
		}
	}

	return &result, nil
}

func (b *backend) pathRoleDelete(
	req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	err := req.Storage.Delete("role/" + data.Get("name").(string))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (b *backend) pathRoleRead(
	req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	role, err := b.getRole(req.Storage, data.Get("name").(string))
	if err != nil {
		return nil, err
	}
	if role == nil {
		return nil, nil
	}

	hasMax := true
	if len(role.MaxTTL) == 0 {
		role.MaxTTL = "(system default)"
		hasMax = false
	}
	if len(role.TTL) == 0 {
		if hasMax {
			role.TTL = "(system default, capped to role max)"
		} else {
			role.TTL = "(system default)"
		}
	}

	resp := &logical.Response{
		Data: structs.New(role).Map(),
	}

	return resp, nil
}

func (b *backend) pathRoleList(
	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	entries, err := req.Storage.List("role/")
	if err != nil {
		return nil, err
	}

	return logical.ListResponse(entries), nil
}

func (b *backend) pathRoleCreate(
	req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	var err error
	name := data.Get("name").(string)

	entry := &roleEntry{
		MaxTTL:              data.Get("max_ttl").(string),
		TTL:                 data.Get("ttl").(string),
		AllowLocalhost:      data.Get("allow_localhost").(bool),
		AllowedDomains:      data.Get("allowed_domains").(string),
		AllowBareDomains:    data.Get("allow_bare_domains").(bool),
		AllowSubdomains:     data.Get("allow_subdomains").(bool),
		AllowAnyName:        data.Get("allow_any_name").(bool),
		EnforceHostnames:    data.Get("enforce_hostnames").(bool),
		AllowIPSANs:         data.Get("allow_ip_sans").(bool),
		ServerFlag:          data.Get("server_flag").(bool),
		ClientFlag:          data.Get("client_flag").(bool),
		CodeSigningFlag:     data.Get("code_signing_flag").(bool),
		EmailProtectionFlag: data.Get("email_protection_flag").(bool),
		KeyType:             data.Get("key_type").(string),
		KeyBits:             data.Get("key_bits").(int),
		UseCSRCommonName:    data.Get("use_csr_common_name").(bool),
	}

	if entry.KeyType == "rsa" && entry.KeyBits < 2048 {
		return logical.ErrorResponse("RSA keys < 2048 bits are unsafe and not supported"), nil
	}

	var maxTTL time.Duration
	maxSystemTTL := b.System().MaxLeaseTTL()
	if len(entry.MaxTTL) == 0 {
		maxTTL = maxSystemTTL
	} else {
		maxTTL, err = time.ParseDuration(entry.MaxTTL)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf(
				"Invalid ttl: %s", err)), nil
		}
	}
	if maxTTL > maxSystemTTL {
		return logical.ErrorResponse("Requested max TTL is higher than backend maximum"), nil
	}

	ttl := b.System().DefaultLeaseTTL()
	if len(entry.TTL) != 0 {
		ttl, err = time.ParseDuration(entry.TTL)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf(
				"Invalid ttl: %s", err)), nil
		}
	}
	if ttl > maxTTL {
		// If they are using the system default, cap it to the role max;
		// if it was specified on the command line, make it an error
		if len(entry.TTL) == 0 {
			entry.TTL = entry.MaxTTL
		} else {
			return logical.ErrorResponse(
				`"ttl" value must be less than "max_ttl" and/or backend default max lease TTL value`,
			), nil
		}
	}

	if errResp := validateKeyTypeLength(entry.KeyType, entry.KeyBits); errResp != nil {
		return errResp, nil
	}

	// Store it
	jsonEntry, err := logical.StorageEntryJSON("role/"+name, entry)
	if err != nil {
		return nil, err
	}
	if err := req.Storage.Put(jsonEntry); err != nil {
		return nil, err
	}

	return nil, nil
}

type roleEntry struct {
	LeaseMax              string `json:"lease_max" structs:"lease_max" mapstructure:"lease_max"`
	Lease                 string `json:"lease" structs:"lease" mapstructure:"lease"`
	MaxTTL                string `json:"max_ttl" structs:"max_ttl" mapstructure:"max_ttl"`
	TTL                   string `json:"ttl" structs:"ttl" mapstructure:"ttl"`
	AllowLocalhost        bool   `json:"allow_localhost" structs:"allow_localhost" mapstructure:"allow_localhost"`
	AllowedBaseDomain     string `json:"allowed_base_domain" structs:"allowed_base_domain" mapstructure:"allowed_base_domain"`
	AllowedDomains        string `json:"allowed_domains" structs:"allowed_domains" mapstructure:"allowed_domains"`
	AllowBaseDomain       bool   `json:"allow_base_domain" structs:"allow_base_domain" mapstructure:"allow_base_domain"`
	AllowBareDomains      bool   `json:"allow_bare_domains" structs:"allow_bare_domains" mapstructure:"allow_bare_domains"`
	AllowTokenDisplayName bool   `json:"allow_token_displayname" structs:"allow_token_displayname" mapstructure:"allow_token_displayname"`
	AllowSubdomains       bool   `json:"allow_subdomains" structs:"allow_subdomains" mapstructure:"allow_subdomains"`
	AllowAnyName          bool   `json:"allow_any_name" structs:"allow_any_name" mapstructure:"allow_any_name"`
	EnforceHostnames      bool   `json:"enforce_hostnames" structs:"enforce_hostnames" mapstructure:"enforce_hostnames"`
	AllowIPSANs           bool   `json:"allow_ip_sans" structs:"allow_ip_sans" mapstructure:"allow_ip_sans"`
	ServerFlag            bool   `json:"server_flag" structs:"server_flag" mapstructure:"server_flag"`
	ClientFlag            bool   `json:"client_flag" structs:"client_flag" mapstructure:"client_flag"`
	CodeSigningFlag       bool   `json:"code_signing_flag" structs:"code_signing_flag" mapstructure:"code_signing_flag"`
	EmailProtectionFlag   bool   `json:"email_protection_flag" structs:"email_protection_flag" mapstructure:"email_protection_flag"`
	UseCSRCommonName      bool   `json:"use_csr_common_name" structs:"use_csr_common_name" mapstructure:"use_csr_common_name"`
	KeyType               string `json:"key_type" structs:"key_type" mapstructure:"key_type"`
	KeyBits               int    `json:"key_bits" structs:"key_bits" mapstructure:"key_bits"`
	MaxPathLength         *int   `json:",omitempty" structs:",omitempty"`
}

const pathRoleHelpSyn = `
Manage the roles that can be created with this backend.
`

const pathRoleHelpDesc = `
This path lets you manage the roles that can be created with this backend.
`
<commit_msg>Store clamped TTLs back in the role's config<commit_after>package pki

import (
	"fmt"
	"strings"
	"time"

	"github.com/fatih/structs"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func pathListRoles(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/?$",

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ListOperation: b.pathRoleList,
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func pathRoles(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/" + framework.GenericNameRegex("name"),
		Fields: map[string]*framework.FieldSchema{
			"name": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "Name of the role",
			},

			"ttl": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "",
				Description: `The lease duration if no specific lease duration is
requested. The lease duration controls the expiration
of certificates issued by this backend. Defaults to
the value of max_ttl.`,
			},

			"max_ttl": &framework.FieldSchema{
				Type:        framework.TypeString,
				Default:     "",
				Description: "The maximum allowed lease duration",
			},

			"allow_localhost": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `Whether to allow "localhost" as a valid common
name in a request`,
			},

			"allowed_domains": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "",
				Description: `If set, clients can request certificates for
subdomains directly beneath these domains, including
the wildcard subdomains. See the documentation for more
information. This parameter accepts a comma-separated list
of domains.`,
			},

			"allow_bare_domains": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, clients can request certificates
for the base domains themselves, e.g. "example.com".
This is a separate option as in some cases this can
be considered a security threat.`,
			},

			"allow_subdomains": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, clients can request certificates for
subdomains of the CNs allowed by the other role options,
including wildcard subdomains. See the documentation for
more information.`,
			},

			"allow_any_name": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, clients can request certificates for
any CN they like. See the documentation for more
information.`,
			},

			"enforce_hostnames": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, only valid host names are allowed for
CN and SANs. Defaults to true.`,
			},

			"allow_ip_sans": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, IP Subject Alternative Names are allowed.
Any valid IP is accepted.`,
			},

			"server_flag": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, certificates are flagged for server auth use.
Defaults to true.`,
			},

			"client_flag": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, certificates are flagged for client auth use.
Defaults to true.`,
			},

			"code_signing_flag": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, certificates are flagged for code signing
use. Defaults to false.`,
			},

			"email_protection_flag": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: false,
				Description: `If set, certificates are flagged for email
protection use. Defaults to false.`,
			},

			"key_type": &framework.FieldSchema{
				Type:    framework.TypeString,
				Default: "rsa",
				Description: `The type of key to use; defaults to RSA. "rsa"
and "ec" are the only valid values.`,
			},

			"key_bits": &framework.FieldSchema{
				Type:    framework.TypeInt,
				Default: 2048,
				Description: `The number of bits to use. You will almost
certainly want to change this if you adjust
the key_type.`,
			},

			"use_csr_common_name": &framework.FieldSchema{
				Type:    framework.TypeBool,
				Default: true,
				Description: `If set, when used with a signing profile,
the common name in the CSR will be used. This
does *not* include any requested Subject Alternative
Names. Defaults to true.`,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ReadOperation:   b.pathRoleRead,
			logical.UpdateOperation: b.pathRoleCreate,
			logical.DeleteOperation: b.pathRoleDelete,
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func (b *backend) getRole(s logical.Storage, n string) (*roleEntry, error) {
	entry, err := s.Get("role/" + n)
	if err != nil {
		return nil, err
	}
	if entry == nil {
		return nil, nil
	}

	var result roleEntry
	if err := entry.DecodeJSON(&result); err != nil {
		return nil, err
	}

	// Migrate existing saved entries and save back if changed
	modified := false
	if len(result.TTL) == 0 && len(result.Lease) != 0 {
		result.TTL = result.Lease
		result.Lease = ""
		modified = true
	}
	if len(result.MaxTTL) == 0 && len(result.LeaseMax) != 0 {
		result.MaxTTL = result.LeaseMax
		result.LeaseMax = ""
		modified = true
	}
	if result.AllowBaseDomain {
		result.AllowBaseDomain = false
		result.AllowBareDomains = true
		modified = true
	}
	if result.AllowedBaseDomain != "" {
		found := false
		allowedDomains := strings.Split(result.AllowedDomains, ",")
		if len(allowedDomains) != 0 {
			for _, v := range allowedDomains {
				if v == result.AllowedBaseDomain {
					found = true
					break
				}
			}
		}
		if !found {
			if result.AllowedDomains == "" {
				result.AllowedDomains = result.AllowedBaseDomain
			} else {
				result.AllowedDomains += "," + result.AllowedBaseDomain
			}
		}
		result.AllowedBaseDomain = ""
		modified = true
	}

	if modified {
		jsonEntry, err := logical.StorageEntryJSON("role/"+n, &result)
		if err != nil {
			return nil, err
		}
		if err := s.Put(jsonEntry); err != nil {
			return nil, err
		}
	}

	return &result, nil
}

func (b *backend) pathRoleDelete(
	req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	err := req.Storage.Delete("role/" + data.Get("name").(string))
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func (b *backend) pathRoleRead(
	req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	role, err := b.getRole(req.Storage, data.Get("name").(string))
	if err != nil {
		return nil, err
	}
	if role == nil {
		return nil, nil
	}

	hasMax := true
	if len(role.MaxTTL) == 0 {
		role.MaxTTL = "(system default)"
		hasMax = false
	}
	if len(role.TTL) == 0 {
		if hasMax {
			role.TTL = "(system default, capped to role max)"
		} else {
			role.TTL = "(system default)"
		}
	}

	resp := &logical.Response{
		Data: structs.New(role).Map(),
	}

	return resp, nil
}

func (b *backend) pathRoleList(
	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	entries, err := req.Storage.List("role/")
	if err != nil {
		return nil, err
	}

	return logical.ListResponse(entries), nil
}

func (b *backend) pathRoleCreate(
	req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
	var err error
	name := data.Get("name").(string)

	entry := &roleEntry{
		MaxTTL:              data.Get("max_ttl").(string),
		TTL:                 data.Get("ttl").(string),
		AllowLocalhost:      data.Get("allow_localhost").(bool),
		AllowedDomains:      data.Get("allowed_domains").(string),
		AllowBareDomains:    data.Get("allow_bare_domains").(bool),
		AllowSubdomains:     data.Get("allow_subdomains").(bool),
		AllowAnyName:        data.Get("allow_any_name").(bool),
		EnforceHostnames:    data.Get("enforce_hostnames").(bool),
		AllowIPSANs:         data.Get("allow_ip_sans").(bool),
		ServerFlag:          data.Get("server_flag").(bool),
		ClientFlag:          data.Get("client_flag").(bool),
		CodeSigningFlag:     data.Get("code_signing_flag").(bool),
		EmailProtectionFlag: data.Get("email_protection_flag").(bool),
		KeyType:             data.Get("key_type").(string),
		KeyBits:             data.Get("key_bits").(int),
		UseCSRCommonName:    data.Get("use_csr_common_name").(bool),
	}

	if entry.KeyType == "rsa" && entry.KeyBits < 2048 {
		return logical.ErrorResponse("RSA keys < 2048 bits are unsafe and not supported"), nil
	}

	var maxTTL time.Duration
	maxSystemTTL := b.System().MaxLeaseTTL()
	if len(entry.MaxTTL) == 0 {
		maxTTL = maxSystemTTL
	} else {
		maxTTL, err = time.ParseDuration(entry.MaxTTL)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf(
				"Invalid ttl: %s", err)), nil
		}
	}
	if maxTTL > maxSystemTTL {
		return logical.ErrorResponse("Requested max TTL is higher than backend maximum"), nil
	}

	ttl := b.System().DefaultLeaseTTL()
	if len(entry.TTL) != 0 {
		ttl, err = time.ParseDuration(entry.TTL)
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf(
				"Invalid ttl: %s", err)), nil
		}
	}
	if ttl > maxTTL {
		// If they are using the system default, cap it to the role max;
		// if it was specified on the command line, make it an error
		if len(entry.TTL) == 0 {
			ttl = maxTTL
		} else {
			return logical.ErrorResponse(
				`"ttl" value must be less than "max_ttl" and/or backend default max lease TTL value`,
			), nil
		}
	}

	// Persist clamped TTLs
	entry.TTL = ttl.String()
	entry.MaxTTL = maxTTL.String()

	if errResp := validateKeyTypeLength(entry.KeyType, entry.KeyBits); errResp != nil {
		return errResp, nil
	}

	// Store it
	jsonEntry, err := logical.StorageEntryJSON("role/"+name, entry)
	if err != nil {
		return nil, err
	}
	if err := req.Storage.Put(jsonEntry); err != nil {
		return nil, err
	}

	return nil, nil
}

type roleEntry struct {
	LeaseMax              string `json:"lease_max" structs:"lease_max" mapstructure:"lease_max"`
	Lease                 string `json:"lease" structs:"lease" mapstructure:"lease"`
	MaxTTL                string `json:"max_ttl" structs:"max_ttl" mapstructure:"max_ttl"`
	TTL                   string `json:"ttl" structs:"ttl" mapstructure:"ttl"`
	AllowLocalhost        bool   `json:"allow_localhost" structs:"allow_localhost" mapstructure:"allow_localhost"`
	AllowedBaseDomain     string `json:"allowed_base_domain" structs:"allowed_base_domain" mapstructure:"allowed_base_domain"`
	AllowedDomains        string `json:"allowed_domains" structs:"allowed_domains" mapstructure:"allowed_domains"`
	AllowBaseDomain       bool   `json:"allow_base_domain" structs:"allow_base_domain" mapstructure:"allow_base_domain"`
	AllowBareDomains      bool   `json:"allow_bare_domains" structs:"allow_bare_domains" mapstructure:"allow_bare_domains"`
	AllowTokenDisplayName bool   `json:"allow_token_displayname" structs:"allow_token_displayname" mapstructure:"allow_token_displayname"`
	AllowSubdomains       bool   `json:"allow_subdomains" structs:"allow_subdomains" mapstructure:"allow_subdomains"`
	AllowAnyName          bool   `json:"allow_any_name" structs:"allow_any_name" mapstructure:"allow_any_name"`
	EnforceHostnames      bool   `json:"enforce_hostnames" structs:"enforce_hostnames" mapstructure:"enforce_hostnames"`
	AllowIPSANs           bool   `json:"allow_ip_sans" structs:"allow_ip_sans" mapstructure:"allow_ip_sans"`
	ServerFlag            bool   `json:"server_flag" structs:"server_flag" mapstructure:"server_flag"`
	ClientFlag            bool   `json:"client_flag" structs:"client_flag" mapstructure:"client_flag"`
	CodeSigningFlag       bool   `json:"code_signing_flag" structs:"code_signing_flag" mapstructure:"code_signing_flag"`
	EmailProtectionFlag   bool   `json:"email_protection_flag" structs:"email_protection_flag" mapstructure:"email_protection_flag"`
	UseCSRCommonName      bool   `json:"use_csr_common_name" structs:"use_csr_common_name" mapstructure:"use_csr_common_name"`
	KeyType               string `json:"key_type" structs:"key_type" mapstructure:"key_type"`
	KeyBits               int    `json:"key_bits" structs:"key_bits" mapstructure:"key_bits"`
	MaxPathLength         *int   `json:",omitempty" structs:",omitempty"`
}

const pathRoleHelpSyn = `
Manage the roles that can be created with this backend.
`

const pathRoleHelpDesc = `
This path lets you manage the roles that can be created with this backend.
`
<|endoftext|>


<commit_before>// Copyright (c) 2013 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 or at
// https://developers.google.com/open-source/licenses/bsd.

// Package lintutil provides helpers for writing linter command lines.
package lintutil // import "honnef.co/go/tools/lint/lintutil"

import (
	"errors"
	"flag"
	"fmt"
	"go/build"
	"go/token"
	"log"
	"os"
	"regexp"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"

	"honnef.co/go/tools/config"
	"honnef.co/go/tools/lint"
	"honnef.co/go/tools/lint/lintutil/format"
	"honnef.co/go/tools/version"

	"golang.org/x/tools/go/packages"
)

func usage(name string, flags *flag.FlagSet) func() {
	return func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name)
		fmt.Fprintf(os.Stderr, "Flags:\n")
		flags.PrintDefaults()
	}
}

func parseIgnore(s string) ([]lint.Ignore, error) {
	var out []lint.Ignore
	if len(s) == 0 {
		return nil, nil
	}
	for _, part := range strings.Fields(s) {
		p := strings.Split(part, ":")
		if len(p) != 2 {
			return nil, errors.New("malformed ignore string")
		}
		path := p[0]
		checks := strings.Split(p[1], ",")
		out = append(out, &lint.GlobIgnore{Pattern: path, Checks: checks})
	}
	return out, nil
}

type versionFlag int

func (v *versionFlag) String() string {
	return fmt.Sprintf("1.%d", *v)
}

func (v *versionFlag) Set(s string) error {
	if len(s) < 3 {
		return errors.New("invalid Go version")
	}
	if s[0] != '1' {
		return errors.New("invalid Go version")
	}
	if s[1] != '.' {
		return errors.New("invalid Go version")
	}
	i, err := strconv.Atoi(s[2:])
	*v = versionFlag(i)
	return err
}

func (v *versionFlag) Get() interface{} {
	return int(*v)
}

type list []string

func (list *list) String() string {
	return `"` + strings.Join(*list, ",") + `"`
}

func (list *list) Set(s string) error {
	if s == "" {
		*list = nil
		return nil
	}

	*list = strings.Split(s, ",")
	return nil
}

func FlagSet(name string) *flag.FlagSet {
	flags := flag.NewFlagSet("", flag.ExitOnError)
	flags.Usage = usage(name, flags)
	flags.String("tags", "", "List of `build tags`")
	flags.String("ignore", "", "Deprecated: use linter directives instead")
	flags.Bool("tests", true, "Include tests")
	flags.Bool("version", false, "Print version and exit")
	flags.Bool("show-ignored", false, "Don't filter ignored problems")
	flags.String("f", "text", "Output `format` (valid choices are 'stylish', 'text' and 'json')")

	flags.Int("debug.max-concurrent-jobs", 0, "Number of jobs to run concurrently")
	flags.Bool("debug.print-stats", false, "Print debug statistics")
	flags.String("debug.cpuprofile", "", "Write CPU profile to `file`")

	checks := list{"inherit"}
	fail := list{"all"}
	flags.Var(&checks, "checks", "Comma-separated list of `checks` to enable.")
	flags.Var(&fail, "fail", "Comma-separated list of `checks` that can cause a non-zero exit status.")

	tags := build.Default.ReleaseTags
	v := tags[len(tags)-1][2:]
	version := new(versionFlag)
	if err := version.Set(v); err != nil {
		panic(fmt.Sprintf("internal error: %s", err))
	}

	flags.Var(version, "go", "Target Go `version` in the format '1.x'")
	return flags
}

func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) {
	tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
	ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string)
	tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
	goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int)
	formatter := fs.Lookup("f").Value.(flag.Getter).Get().(string)
	printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
	showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)

	maxConcurrentJobs := fs.Lookup("debug.max-concurrent-jobs").Value.(flag.Getter).Get().(int)
	printStats := fs.Lookup("debug.print-stats").Value.(flag.Getter).Get().(bool)
	cpuProfile := fs.Lookup("debug.cpuprofile").Value.(flag.Getter).Get().(string)

	cfg := config.Config{}
	cfg.Checks = *fs.Lookup("checks").Value.(*list)

	exit := os.Exit
	if cpuProfile != "" {
		f, err := os.Create(cpuProfile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		exit = func(code int) {
			pprof.StopCPUProfile()
			os.Exit(code)
		}
	}

	if printVersion {
		version.Print()
		exit(0)
	}

	ps, err := Lint(cs, fs.Args(), &Options{
		Tags:          strings.Fields(tags),
		LintTests:     tests,
		Ignores:       ignore,
		GoVersion:     goVersion,
		ReturnIgnored: showIgnored,
		Config:        cfg,

		MaxConcurrentJobs: maxConcurrentJobs,
		PrintStats:        printStats,
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		exit(1)
	}

	var f format.Formatter
	switch formatter {
	case "text":
		f = format.Text{W: os.Stdout}
	case "stylish":
		f = &format.Stylish{W: os.Stdout}
	case "json":
		f = format.JSON{W: os.Stdout}
	default:
		fmt.Fprintf(os.Stderr, "unsupported output format %q\n", formatter)
		exit(2)
	}

	var (
		total    int
		errors   int
		warnings int
	)

	fail := *fs.Lookup("fail").Value.(*list)
	var allChecks []string
	for _, p := range ps {
		allChecks = append(allChecks, p.Check)
	}

	shouldExit := lint.FilterChecks(allChecks, fail)

	total = len(ps)
	for _, p := range ps {
		if shouldExit[p.Check] {
			errors++
		} else {
			p.Severity = lint.Warning
			warnings++
		}
		f.Format(p)
	}
	if f, ok := f.(format.Statter); ok {
		f.Stats(total, errors, warnings)
	}
	if errors > 0 {
		exit(1)
	}
}

type Options struct {
	Config config.Config

	Tags          []string
	LintTests     bool
	Ignores       string
	GoVersion     int
	ReturnIgnored bool

	MaxConcurrentJobs int
	PrintStats        bool
}

func Lint(cs []lint.Checker, paths []string, opt *Options) ([]lint.Problem, error) {
	stats := lint.PerfStats{
		CheckerInits: map[string]time.Duration{},
	}

	if opt == nil {
		opt = &Options{}
	}
	ignores, err := parseIgnore(opt.Ignores)
	if err != nil {
		return nil, err
	}

	ctx := build.Default
	// XXX nothing cares about built tags right now
	ctx.BuildTags = opt.Tags
	conf := &packages.Config{
		Mode:  packages.LoadAllSyntax,
		Tests: opt.LintTests,
	}

	t := time.Now()
	if len(paths) == 0 {
		paths = []string{"."}
	}
	pkgs, err := packages.Load(conf, paths...)
	if err != nil {
		return nil, err
	}
	stats.PackageLoading = time.Since(t)

	var problems []lint.Problem
	workingPkgs := make([]*packages.Package, 0, len(pkgs))
	for _, pkg := range pkgs {
		if pkg.IllTyped {
			problems = append(problems, compileErrors(pkg)...)
		} else {
			workingPkgs = append(workingPkgs, pkg)
		}
	}

	if len(workingPkgs) == 0 {
		return problems, nil
	}

	l := &lint.Linter{
		Checkers:      cs,
		Ignores:       ignores,
		GoVersion:     opt.GoVersion,
		ReturnIgnored: opt.ReturnIgnored,
		Config:        opt.Config,

		MaxConcurrentJobs: opt.MaxConcurrentJobs,
		PrintStats:        opt.PrintStats,
	}
	problems = append(problems, l.Lint(workingPkgs, &stats)...)

	return problems, nil
}

var posRe = regexp.MustCompile(`^(.+?):(\d+):(\d+)?$`)

func parsePos(pos string) token.Position {
	if pos == "-" || pos == "" {
		return token.Position{}
	}
	parts := posRe.FindStringSubmatch(pos)
	if parts == nil {
		panic(fmt.Sprintf("internal error: malformed position %q", pos))
	}
	file := parts[0]
	line, _ := strconv.Atoi(parts[1])
	col, _ := strconv.Atoi(parts[2])
	return token.Position{
		Filename: file,
		Line:     line,
		Column:   col,
	}
}

func compileErrors(pkg *packages.Package) []lint.Problem {
	if !pkg.IllTyped {
		return nil
	}
	if len(pkg.Errors) == 0 {
		// transitively ill-typed
		var ps []lint.Problem
		for _, imp := range pkg.Imports {
			ps = append(ps, compileErrors(imp)...)
		}
		return ps
	}
	var ps []lint.Problem
	for _, err := range pkg.Errors {
		p := lint.Problem{
			Position: parsePos(err.Pos),
			Text:     err.Msg,
			Checker:  "compiler",
			Check:    "compile",
		}
		ps = append(ps, p)
	}
	return ps
}

func ProcessArgs(name string, cs []lint.Checker, args []string) {
	flags := FlagSet(name)
	flags.Parse(args)

	ProcessFlagSet(cs, flags)
}
<commit_msg>lint/lintutil: add flag for writing memory profiles<commit_after>// Copyright (c) 2013 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 or at
// https://developers.google.com/open-source/licenses/bsd.

// Package lintutil provides helpers for writing linter command lines.
package lintutil // import "honnef.co/go/tools/lint/lintutil"

import (
	"errors"
	"flag"
	"fmt"
	"go/build"
	"go/token"
	"log"
	"os"
	"regexp"
	"runtime"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"

	"honnef.co/go/tools/config"
	"honnef.co/go/tools/lint"
	"honnef.co/go/tools/lint/lintutil/format"
	"honnef.co/go/tools/version"

	"golang.org/x/tools/go/packages"
)

func usage(name string, flags *flag.FlagSet) func() {
	return func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name)
		fmt.Fprintf(os.Stderr, "Flags:\n")
		flags.PrintDefaults()
	}
}

func parseIgnore(s string) ([]lint.Ignore, error) {
	var out []lint.Ignore
	if len(s) == 0 {
		return nil, nil
	}
	for _, part := range strings.Fields(s) {
		p := strings.Split(part, ":")
		if len(p) != 2 {
			return nil, errors.New("malformed ignore string")
		}
		path := p[0]
		checks := strings.Split(p[1], ",")
		out = append(out, &lint.GlobIgnore{Pattern: path, Checks: checks})
	}
	return out, nil
}

type versionFlag int

func (v *versionFlag) String() string {
	return fmt.Sprintf("1.%d", *v)
}

func (v *versionFlag) Set(s string) error {
	if len(s) < 3 {
		return errors.New("invalid Go version")
	}
	if s[0] != '1' {
		return errors.New("invalid Go version")
	}
	if s[1] != '.' {
		return errors.New("invalid Go version")
	}
	i, err := strconv.Atoi(s[2:])
	*v = versionFlag(i)
	return err
}

func (v *versionFlag) Get() interface{} {
	return int(*v)
}

type list []string

func (list *list) String() string {
	return `"` + strings.Join(*list, ",") + `"`
}

func (list *list) Set(s string) error {
	if s == "" {
		*list = nil
		return nil
	}

	*list = strings.Split(s, ",")
	return nil
}

func FlagSet(name string) *flag.FlagSet {
	flags := flag.NewFlagSet("", flag.ExitOnError)
	flags.Usage = usage(name, flags)
	flags.String("tags", "", "List of `build tags`")
	flags.String("ignore", "", "Deprecated: use linter directives instead")
	flags.Bool("tests", true, "Include tests")
	flags.Bool("version", false, "Print version and exit")
	flags.Bool("show-ignored", false, "Don't filter ignored problems")
	flags.String("f", "text", "Output `format` (valid choices are 'stylish', 'text' and 'json')")

	flags.Int("debug.max-concurrent-jobs", 0, "Number of jobs to run concurrently")
	flags.Bool("debug.print-stats", false, "Print debug statistics")
	flags.String("debug.cpuprofile", "", "Write CPU profile to `file`")
	flags.String("debug.memprofile", "", "Write memory profile to `file`")

	checks := list{"inherit"}
	fail := list{"all"}
	flags.Var(&checks, "checks", "Comma-separated list of `checks` to enable.")
	flags.Var(&fail, "fail", "Comma-separated list of `checks` that can cause a non-zero exit status.")

	tags := build.Default.ReleaseTags
	v := tags[len(tags)-1][2:]
	version := new(versionFlag)
	if err := version.Set(v); err != nil {
		panic(fmt.Sprintf("internal error: %s", err))
	}

	flags.Var(version, "go", "Target Go `version` in the format '1.x'")
	return flags
}

func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) {
	tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
	ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string)
	tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
	goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int)
	formatter := fs.Lookup("f").Value.(flag.Getter).Get().(string)
	printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
	showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)

	maxConcurrentJobs := fs.Lookup("debug.max-concurrent-jobs").Value.(flag.Getter).Get().(int)
	printStats := fs.Lookup("debug.print-stats").Value.(flag.Getter).Get().(bool)
	cpuProfile := fs.Lookup("debug.cpuprofile").Value.(flag.Getter).Get().(string)
	memProfile := fs.Lookup("debug.memprofile").Value.(flag.Getter).Get().(string)

	cfg := config.Config{}
	cfg.Checks = *fs.Lookup("checks").Value.(*list)

	exit := func(code int) {
		if cpuProfile != "" {
			pprof.StopCPUProfile()
		}
		if memProfile != "" {
			f, err := os.Create(memProfile)
			if err != nil {
				panic(err)
			}
			runtime.GC()
			pprof.WriteHeapProfile(f)
		}
		os.Exit(code)
	}
	if cpuProfile != "" {
		f, err := os.Create(cpuProfile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
	}

	if printVersion {
		version.Print()
		exit(0)
	}

	ps, err := Lint(cs, fs.Args(), &Options{
		Tags:          strings.Fields(tags),
		LintTests:     tests,
		Ignores:       ignore,
		GoVersion:     goVersion,
		ReturnIgnored: showIgnored,
		Config:        cfg,

		MaxConcurrentJobs: maxConcurrentJobs,
		PrintStats:        printStats,
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		exit(1)
	}

	var f format.Formatter
	switch formatter {
	case "text":
		f = format.Text{W: os.Stdout}
	case "stylish":
		f = &format.Stylish{W: os.Stdout}
	case "json":
		f = format.JSON{W: os.Stdout}
	default:
		fmt.Fprintf(os.Stderr, "unsupported output format %q\n", formatter)
		exit(2)
	}

	var (
		total    int
		errors   int
		warnings int
	)

	fail := *fs.Lookup("fail").Value.(*list)
	var allChecks []string
	for _, p := range ps {
		allChecks = append(allChecks, p.Check)
	}

	shouldExit := lint.FilterChecks(allChecks, fail)

	total = len(ps)
	for _, p := range ps {
		if shouldExit[p.Check] {
			errors++
		} else {
			p.Severity = lint.Warning
			warnings++
		}
		f.Format(p)
	}
	if f, ok := f.(format.Statter); ok {
		f.Stats(total, errors, warnings)
	}
	if errors > 0 {
		exit(1)
	}
}

type Options struct {
	Config config.Config

	Tags          []string
	LintTests     bool
	Ignores       string
	GoVersion     int
	ReturnIgnored bool

	MaxConcurrentJobs int
	PrintStats        bool
}

func Lint(cs []lint.Checker, paths []string, opt *Options) ([]lint.Problem, error) {
	stats := lint.PerfStats{
		CheckerInits: map[string]time.Duration{},
	}

	if opt == nil {
		opt = &Options{}
	}
	ignores, err := parseIgnore(opt.Ignores)
	if err != nil {
		return nil, err
	}

	ctx := build.Default
	// XXX nothing cares about built tags right now
	ctx.BuildTags = opt.Tags
	conf := &packages.Config{
		Mode:  packages.LoadAllSyntax,
		Tests: opt.LintTests,
	}

	t := time.Now()
	if len(paths) == 0 {
		paths = []string{"."}
	}
	pkgs, err := packages.Load(conf, paths...)
	if err != nil {
		return nil, err
	}
	stats.PackageLoading = time.Since(t)

	var problems []lint.Problem
	workingPkgs := make([]*packages.Package, 0, len(pkgs))
	for _, pkg := range pkgs {
		if pkg.IllTyped {
			problems = append(problems, compileErrors(pkg)...)
		} else {
			workingPkgs = append(workingPkgs, pkg)
		}
	}

	if len(workingPkgs) == 0 {
		return problems, nil
	}

	l := &lint.Linter{
		Checkers:      cs,
		Ignores:       ignores,
		GoVersion:     opt.GoVersion,
		ReturnIgnored: opt.ReturnIgnored,
		Config:        opt.Config,

		MaxConcurrentJobs: opt.MaxConcurrentJobs,
		PrintStats:        opt.PrintStats,
	}
	problems = append(problems, l.Lint(workingPkgs, &stats)...)

	return problems, nil
}

var posRe = regexp.MustCompile(`^(.+?):(\d+):(\d+)?$`)

func parsePos(pos string) token.Position {
	if pos == "-" || pos == "" {
		return token.Position{}
	}
	parts := posRe.FindStringSubmatch(pos)
	if parts == nil {
		panic(fmt.Sprintf("internal error: malformed position %q", pos))
	}
	file := parts[0]
	line, _ := strconv.Atoi(parts[1])
	col, _ := strconv.Atoi(parts[2])
	return token.Position{
		Filename: file,
		Line:     line,
		Column:   col,
	}
}

func compileErrors(pkg *packages.Package) []lint.Problem {
	if !pkg.IllTyped {
		return nil
	}
	if len(pkg.Errors) == 0 {
		// transitively ill-typed
		var ps []lint.Problem
		for _, imp := range pkg.Imports {
			ps = append(ps, compileErrors(imp)...)
		}
		return ps
	}
	var ps []lint.Problem
	for _, err := range pkg.Errors {
		p := lint.Problem{
			Position: parsePos(err.Pos),
			Text:     err.Msg,
			Checker:  "compiler",
			Check:    "compile",
		}
		ps = append(ps, p)
	}
	return ps
}

func ProcessArgs(name string, cs []lint.Checker, args []string) {
	flags := FlagSet(name)
	flags.Parse(args)

	ProcessFlagSet(cs, flags)
}
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// deploy command tests
// Author: bratseth

package cmd

import (
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/vespa-engine/vespa/client/go/mock"
	"github.com/vespa-engine/vespa/client/go/vespa"
)

func TestPrepareZip(t *testing.T) {
	assertPrepare("testdata/applications/withTarget/target/application.zip",
		[]string{"prepare", "testdata/applications/withTarget/target/application.zip"}, t)
}

func TestActivateZip(t *testing.T) {
	assertActivate("testdata/applications/withTarget/target/application.zip",
		[]string{"activate", "testdata/applications/withTarget/target/application.zip"}, t)
}

func TestDeployZip(t *testing.T) {
	assertDeploy("testdata/applications/withTarget/target/application.zip",
		[]string{"deploy", "testdata/applications/withTarget/target/application.zip"}, t)
}

func TestDeployZipWithURLTargetArgument(t *testing.T) {
	applicationPackage := "testdata/applications/withTarget/target/application.zip"
	arguments := []string{"deploy", "testdata/applications/withTarget/target/application.zip", "-t", "http://target:19071"}

	client := &mock.HTTPClient{}
	cli, stdout, _ := newTestCLI(t)
	cli.httpClient = client
	assert.Nil(t, cli.Run(arguments...))
	assert.Equal(t,
		"\nSuccess: Deployed "+applicationPackage+"\n",
		stdout.String())
	assertDeployRequestMade("http://target:19071", client, t)
}

func TestDeployZipWitLocalTargetArgument(t *testing.T) {
	assertDeploy("testdata/applications/withTarget/target/application.zip",
		[]string{"deploy", "testdata/applications/withTarget/target/application.zip", "-t", "local"}, t)
}

func TestDeploySourceDirectory(t *testing.T) {
	assertDeploy("testdata/applications/withSource/src/main/application",
		[]string{"deploy", "testdata/applications/withSource/src/main/application"}, t)
}

func TestDeployApplicationDirectoryWithSource(t *testing.T) {
	assertDeploy("testdata/applications/withSource/src/main/application",
		[]string{"deploy", "testdata/applications/withSource"}, t)
}

func TestDeployApplicationDirectoryWithPomAndTarget(t *testing.T) {
	assertDeploy("testdata/applications/withTarget/target/application.zip",
		[]string{"deploy", "testdata/applications/withTarget"}, t)
}

func TestDeployApplicationDirectoryWithPomAndEmptyTarget(t *testing.T) {
	cli, _, stderr := newTestCLI(t)
	assert.NotNil(t, cli.Run("deploy", "testdata/applications/withEmptyTarget"))
	assert.Equal(t,
		"Error: pom.xml exists but no target/application.zip. Run mvn package first\n",
		stderr.String())
}

func TestDeployApplicationPackageErrorWithUnexpectedNonJson(t *testing.T) {
	assertApplicationPackageError(t, "deploy", 401,
		"Raw text error",
		"Raw text error")
}

func TestDeployApplicationPackageErrorWithUnexpectedJson(t *testing.T) {
	assertApplicationPackageError(t, "deploy", 401,
		`{
    "some-unexpected-json": "Invalid XML, error in services.xml: element \"nosuch\" not allowed here"
}`,
		`{"some-unexpected-json": "Invalid XML, error in services.xml: element \"nosuch\" not allowed here"}`)
}

func TestDeployApplicationPackageErrorWithExpectedFormat(t *testing.T) {
	assertApplicationPackageError(t, "deploy", 400,
		"Invalid XML, error in services.xml:\nelement \"nosuch\" not allowed here",
		`{
         "error-code": "INVALID_APPLICATION_PACKAGE",
         "message": "Invalid XML, error in services.xml: element \"nosuch\" not allowed here"
     }`)
}

func TestPrepareApplicationPackageErrorWithExpectedFormat(t *testing.T) {
	assertApplicationPackageError(t, "prepare", 400,
		"Invalid XML, error in services.xml:\nelement \"nosuch\" not allowed here",
		`{
         "error-code": "INVALID_APPLICATION_PACKAGE",
         "message": "Invalid XML, error in services.xml: element \"nosuch\" not allowed here"
     }`)
}

func TestDeployError(t *testing.T) {
	assertDeployServerError(t, 501, "Deploy service error")
}

func assertDeploy(applicationPackage string, arguments []string, t *testing.T) {
	cli, stdout, _ := newTestCLI(t)
	client := &mock.HTTPClient{}
	cli.httpClient = client
	assert.Nil(t, cli.Run(arguments...))
	assert.Equal(t,
		"\nSuccess: Deployed "+applicationPackage+"\n",
		stdout.String())
	assertDeployRequestMade("http://127.0.0.1:19071", client, t)
}

func assertPrepare(applicationPackage string, arguments []string, t *testing.T) {
	client := &mock.HTTPClient{}
	client.NextResponseString(200, `{"session-id":"42"}`)
	cli, stdout, _ := newTestCLI(t)
	cli.httpClient = client
	assert.Nil(t, cli.Run(arguments...))
	assert.Equal(t,
		"Success: Prepared "+applicationPackage+" with session 42\n",
		stdout.String())

	assertPackageUpload(0, "http://127.0.0.1:19071/application/v2/tenant/default/session", client, t)
	sessionURL := "http://127.0.0.1:19071/application/v2/tenant/default/session/42/prepared"
	assert.Equal(t, sessionURL, client.Requests[1].URL.String())
	assert.Equal(t, "PUT", client.Requests[1].Method)
}

func assertActivate(applicationPackage string, arguments []string, t *testing.T) {
	client := &mock.HTTPClient{}
	cli, stdout, _ := newTestCLI(t)
	cli.httpClient = client
	if err := cli.config.writeSessionID(vespa.DefaultApplication, 42); err != nil {
		t.Fatal(err)
	}
	assert.Nil(t, cli.Run(arguments...))
	assert.Equal(t,
		"Success: Activated "+applicationPackage+" with session 42\n",
		stdout.String())
	url := "http://127.0.0.1:19071/application/v2/tenant/default/session/42/active"
	assert.Equal(t, url, client.LastRequest.URL.String())
	assert.Equal(t, "PUT", client.LastRequest.Method)
}

func assertPackageUpload(requestNumber int, url string, client *mock.HTTPClient, t *testing.T) {
	req := client.LastRequest
	if requestNumber >= 0 {
		req = client.Requests[requestNumber]
	}
	assert.Equal(t, url, req.URL.String())
	assert.Equal(t, "application/zip", req.Header.Get("Content-Type"))
	assert.Equal(t, "POST", req.Method)
	var body = req.Body
	assert.NotNil(t, body)
	buf := make([]byte, 7) // Just check the first few bytes
	body.Read(buf)
	assert.Equal(t, "PK\x03\x04\x14\x00\b", string(buf))
}

func assertDeployRequestMade(target string, client *mock.HTTPClient, t *testing.T) {
	assertPackageUpload(-1, target+"/application/v2/tenant/default/prepareandactivate", client, t)
}

func assertApplicationPackageError(t *testing.T, cmd string, status int, expectedMessage string, returnBody string) {
	client := &mock.HTTPClient{}
	client.NextResponseString(status, returnBody)
	cli, _, stderr := newTestCLI(t)
	cli.httpClient = client
	assert.NotNil(t, cli.Run(cmd, "testdata/applications/withTarget/target/application.zip"))
	assert.Equal(t,
		"Error: invalid application package (Status "+strconv.Itoa(status)+")\n"+expectedMessage+"\n",
		stderr.String())
}

func assertDeployServerError(t *testing.T, status int, errorMessage string) {
	client := &mock.HTTPClient{}
	client.NextResponseString(status, errorMessage)
	cli, _, stderr := newTestCLI(t)
	cli.httpClient = client
	assert.NotNil(t, cli.Run("deploy", "testdata/applications/withTarget/target/application.zip"))
	assert.Equal(t,
		"Error: error from deploy api at 127.0.0.1:19071 (Status "+strconv.Itoa(status)+"):\n"+errorMessage+"\n",
		stderr.String())
}
<commit_msg>Mark test helpers<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// deploy command tests
// Author: bratseth

package cmd

import (
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/vespa-engine/vespa/client/go/mock"
	"github.com/vespa-engine/vespa/client/go/vespa"
)

func TestPrepareZip(t *testing.T) {
	assertPrepare("testdata/applications/withTarget/target/application.zip",
		[]string{"prepare", "testdata/applications/withTarget/target/application.zip"}, t)
}

func TestActivateZip(t *testing.T) {
	assertActivate("testdata/applications/withTarget/target/application.zip",
		[]string{"activate", "testdata/applications/withTarget/target/application.zip"}, t)
}

func TestDeployZip(t *testing.T) {
	assertDeploy("testdata/applications/withTarget/target/application.zip",
		[]string{"deploy", "testdata/applications/withTarget/target/application.zip"}, t)
}

func TestDeployZipWithURLTargetArgument(t *testing.T) {
	applicationPackage := "testdata/applications/withTarget/target/application.zip"
	arguments := []string{"deploy", "testdata/applications/withTarget/target/application.zip", "-t", "http://target:19071"}

	client := &mock.HTTPClient{}
	cli, stdout, _ := newTestCLI(t)
	cli.httpClient = client
	assert.Nil(t, cli.Run(arguments...))
	assert.Equal(t,
		"\nSuccess: Deployed "+applicationPackage+"\n",
		stdout.String())
	assertDeployRequestMade("http://target:19071", client, t)
}

func TestDeployZipWitLocalTargetArgument(t *testing.T) {
	assertDeploy("testdata/applications/withTarget/target/application.zip",
		[]string{"deploy", "testdata/applications/withTarget/target/application.zip", "-t", "local"}, t)
}

func TestDeploySourceDirectory(t *testing.T) {
	assertDeploy("testdata/applications/withSource/src/main/application",
		[]string{"deploy", "testdata/applications/withSource/src/main/application"}, t)
}

func TestDeployApplicationDirectoryWithSource(t *testing.T) {
	assertDeploy("testdata/applications/withSource/src/main/application",
		[]string{"deploy", "testdata/applications/withSource"}, t)
}

func TestDeployApplicationDirectoryWithPomAndTarget(t *testing.T) {
	assertDeploy("testdata/applications/withTarget/target/application.zip",
		[]string{"deploy", "testdata/applications/withTarget"}, t)
}

func TestDeployApplicationDirectoryWithPomAndEmptyTarget(t *testing.T) {
	cli, _, stderr := newTestCLI(t)
	assert.NotNil(t, cli.Run("deploy", "testdata/applications/withEmptyTarget"))
	assert.Equal(t,
		"Error: pom.xml exists but no target/application.zip. Run mvn package first\n",
		stderr.String())
}

func TestDeployApplicationPackageErrorWithUnexpectedNonJson(t *testing.T) {
	assertApplicationPackageError(t, "deploy", 401,
		"Raw text error",
		"Raw text error")
}

func TestDeployApplicationPackageErrorWithUnexpectedJson(t *testing.T) {
	assertApplicationPackageError(t, "deploy", 401,
		`{
    "some-unexpected-json": "Invalid XML, error in services.xml: element \"nosuch\" not allowed here"
}`,
		`{"some-unexpected-json": "Invalid XML, error in services.xml: element \"nosuch\" not allowed here"}`)
}

func TestDeployApplicationPackageErrorWithExpectedFormat(t *testing.T) {
	assertApplicationPackageError(t, "deploy", 400,
		"Invalid XML, error in services.xml:\nelement \"nosuch\" not allowed here",
		`{
         "error-code": "INVALID_APPLICATION_PACKAGE",
         "message": "Invalid XML, error in services.xml: element \"nosuch\" not allowed here"
     }`)
}

func TestPrepareApplicationPackageErrorWithExpectedFormat(t *testing.T) {
	assertApplicationPackageError(t, "prepare", 400,
		"Invalid XML, error in services.xml:\nelement \"nosuch\" not allowed here",
		`{
         "error-code": "INVALID_APPLICATION_PACKAGE",
         "message": "Invalid XML, error in services.xml: element \"nosuch\" not allowed here"
     }`)
}

func TestDeployError(t *testing.T) {
	assertDeployServerError(t, 501, "Deploy service error")
}

func assertDeploy(applicationPackage string, arguments []string, t *testing.T) {
	t.Helper()
	cli, stdout, _ := newTestCLI(t)
	client := &mock.HTTPClient{}
	cli.httpClient = client
	assert.Nil(t, cli.Run(arguments...))
	assert.Equal(t,
		"\nSuccess: Deployed "+applicationPackage+"\n",
		stdout.String())
	assertDeployRequestMade("http://127.0.0.1:19071", client, t)
}

func assertPrepare(applicationPackage string, arguments []string, t *testing.T) {
	t.Helper()
	client := &mock.HTTPClient{}
	client.NextResponseString(200, `{"session-id":"42"}`)
	cli, stdout, _ := newTestCLI(t)
	cli.httpClient = client
	assert.Nil(t, cli.Run(arguments...))
	assert.Equal(t,
		"Success: Prepared "+applicationPackage+" with session 42\n",
		stdout.String())

	assertPackageUpload(0, "http://127.0.0.1:19071/application/v2/tenant/default/session", client, t)
	sessionURL := "http://127.0.0.1:19071/application/v2/tenant/default/session/42/prepared"
	assert.Equal(t, sessionURL, client.Requests[1].URL.String())
	assert.Equal(t, "PUT", client.Requests[1].Method)
}

func assertActivate(applicationPackage string, arguments []string, t *testing.T) {
	t.Helper()
	client := &mock.HTTPClient{}
	cli, stdout, _ := newTestCLI(t)
	cli.httpClient = client
	if err := cli.config.writeSessionID(vespa.DefaultApplication, 42); err != nil {
		t.Fatal(err)
	}
	assert.Nil(t, cli.Run(arguments...))
	assert.Equal(t,
		"Success: Activated "+applicationPackage+" with session 42\n",
		stdout.String())
	url := "http://127.0.0.1:19071/application/v2/tenant/default/session/42/active"
	assert.Equal(t, url, client.LastRequest.URL.String())
	assert.Equal(t, "PUT", client.LastRequest.Method)
}

func assertPackageUpload(requestNumber int, url string, client *mock.HTTPClient, t *testing.T) {
	t.Helper()
	req := client.LastRequest
	if requestNumber >= 0 {
		req = client.Requests[requestNumber]
	}
	assert.Equal(t, url, req.URL.String())
	assert.Equal(t, "application/zip", req.Header.Get("Content-Type"))
	assert.Equal(t, "POST", req.Method)
	var body = req.Body
	assert.NotNil(t, body)
	buf := make([]byte, 7) // Just check the first few bytes
	body.Read(buf)
	assert.Equal(t, "PK\x03\x04\x14\x00\b", string(buf))
}

func assertDeployRequestMade(target string, client *mock.HTTPClient, t *testing.T) {
	t.Helper()
	assertPackageUpload(-1, target+"/application/v2/tenant/default/prepareandactivate", client, t)
}

func assertApplicationPackageError(t *testing.T, cmd string, status int, expectedMessage string, returnBody string) {
	t.Helper()
	client := &mock.HTTPClient{}
	client.NextResponseString(status, returnBody)
	cli, _, stderr := newTestCLI(t)
	cli.httpClient = client
	assert.NotNil(t, cli.Run(cmd, "testdata/applications/withTarget/target/application.zip"))
	assert.Equal(t,
		"Error: invalid application package (Status "+strconv.Itoa(status)+")\n"+expectedMessage+"\n",
		stderr.String())
}

func assertDeployServerError(t *testing.T, status int, errorMessage string) {
	t.Helper()
	client := &mock.HTTPClient{}
	client.NextResponseString(status, errorMessage)
	cli, _, stderr := newTestCLI(t)
	cli.httpClient = client
	assert.NotNil(t, cli.Run("deploy", "testdata/applications/withTarget/target/application.zip"))
	assert.Equal(t,
		"Error: error from deploy api at 127.0.0.1:19071 (Status "+strconv.Itoa(status)+"):\n"+errorMessage+"\n",
		stderr.String())
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/eternnoir/gcp"
	pool "github.com/eternnoir/gncp"
	"net"
	"reflect"
	"time"
)

var TimeOutError = errors.New("Send data timeout.")

type SocketSender struct {
	logEntry *log.Entry
	basegcp  *gcp.Gcp
	connpool pool.ConnPool
	Host     string
	Port     string
	MinConn  int
	MaxConn  int
}

func InitSocketSender(bgcp *gcp.Gcp, host, port string, minconn, maxconn int) (*SocketSender, error) {
	sender := new(SocketSender)
	sender.Host = host
	sender.Port = port
	sender.MaxConn = maxconn
	sender.MinConn = minconn
	sender.basegcp = bgcp
	sender.logEntry = bgcp.Logger.WithFields(log.Fields{
		"module": "SocketSender",
	})
	factory := func() (net.Conn, error) { return net.Dial("tcp", host+":"+port) }
	p, err := pool.NewPool(minconn, maxconn, factory)
	if err != nil {
		return nil, err
	}
	sender.connpool = p
	return sender, nil
}

func (sender *SocketSender) Start() error {
	return nil
}

func (sender *SocketSender) Stop() error {
	return nil
}

func (sender *SocketSender) Send(context interface{}, timeout int) (interface{}, error) {
	binaryary, ok := context.([]byte)
	if !ok {
		return nil, errors.New(fmt.Sprintf("SocketSender cast process context error. It should be []byte but %s", reflect.TypeOf(context)))
	}
	return sender.processSendRequest(binaryary, timeout)
}

func (sender *SocketSender) processSendRequest(data []byte, timeout int) ([]byte, error) {
	result := make(chan []byte, 1)
	errc := make(chan error, 1)
	go func() {
		resultba, err := sender.fireRequest(data, timeout)
		if err != nil {
			sender.logEntry.Debugf("SocketSender Get connection error.%s", err)
			errc <- err
			return
		}
		result <- resultba
	}()
	select {
	case err := <-errc:
		return nil, err
	case res := <-result:
		return res, nil
	case <-time.After(time.Millisecond * time.Duration(timeout)):
		return nil, TimeOutError
	}
}

func (sender *SocketSender) getConn(timeout int) (net.Conn, error) {
	return sender.connpool.GetWithTimeout(time.Duration(timeout) * time.Millisecond)
}

func (sender *SocketSender) fireRequest(data []byte, timeout int) ([]byte, error) {
	conn, err := sender.getConn(timeout / 2)
	if err != nil {
		return nil, err
	}
	defer conn.Close()
	_, err = conn.Write(data)
	if err != nil {
		sender.logEntry.Errorf("SocketSender Send data error. %s", err)
		return nil, err
	}

	lenbuffer := make([]byte, 2)
	_, err = conn.Read(lenbuffer)

	if err != nil {
		sender.logEntry.Error(conn.RemoteAddr().String(), " connection error: ", err)
		return nil, err
	}

	bytelen := decodeLen(lenbuffer)

	buffer := make([]byte, bytelen)
	_, err2 := conn.Read(buffer)
	sender.logEntry.Debugf("Socket Sender Receive data : %s", hex.EncodeToString(buffer))

	if err2 != nil {
		sender.logEntry.Error(conn.RemoteAddr().String(), " connection error: ", err)
		return nil, err
	}
	return buffer, nil
}

func decodeLen(bytes []byte) uint16 {
	return binary.BigEndian.Uint16(bytes)
}
<commit_msg>Update sender.<commit_after>package lib

import (
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/eternnoir/gcp"
	pool "github.com/eternnoir/gncp"
	"net"
	"reflect"
	"time"
)

var TimeOutError = errors.New("Send data timeout.")

type SocketSender struct {
	logEntry *log.Entry
	basegcp  *gcp.Gcp
	connpool pool.ConnPool
	Host     string
	Port     string
	MinConn  int
	MaxConn  int
}

func InitSocketSender(bgcp *gcp.Gcp, host, port string, minconn, maxconn int) (*SocketSender, error) {
	sender := new(SocketSender)
	sender.Host = host
	sender.Port = port
	sender.MaxConn = maxconn
	sender.MinConn = minconn
	sender.basegcp = bgcp
	sender.logEntry = bgcp.Logger.WithFields(log.Fields{
		"module": "SocketSender",
	})
	err := sender.initConnectionPool()
	if err != nil {
		return nil, err
	}
	return sender, nil
}

func (sender *SocketSender) Start() error {
	return nil
}

func (sender *SocketSender) Stop() error {
	return nil
}

func (sender *SocketSender) Send(context interface{}, timeout int) (interface{}, error) {
	binaryary, ok := context.([]byte)
	if !ok {
		return nil, errors.New(fmt.Sprintf("SocketSender cast process context error. It should be []byte but %s", reflect.TypeOf(context)))
	}
	return sender.processSendRequest(binaryary, timeout)
}

func (sender *SocketSender) processSendRequest(data []byte, timeout int) ([]byte, error) {
	result := make(chan []byte, 1)
	errc := make(chan error, 1)
	go func() {
		resultba, err := sender.fireRequest(data, timeout)
		if err != nil {
			sender.logEntry.Debugf("SocketSender Get connection error.%s", err)
			errc <- err
			return
		}
		result <- resultba
	}()
	select {
	case err := <-errc:
		return nil, err
	case res := <-result:
		return res, nil
	case <-time.After(time.Millisecond * time.Duration(timeout)):
		return nil, TimeOutError
	}
}

func (sender *SocketSender) getConn(timeout int) (net.Conn, error) {
	return sender.connpool.GetWithTimeout(time.Duration(timeout) * time.Millisecond)
}

func (sender *SocketSender) fireRequest(data []byte, timeout int) ([]byte, error) {
	if sender.connpool == nil {
		return nil, errors.New("Connection Pool error.")
	}
	conn, err := sender.getConn(timeout / 2)
	if err != nil {
		return nil, err
	}
	defer conn.Close()
	_, err = conn.Write(data)
	if err != nil {
		sender.logEntry.Errorf("SocketSender Send data error. %s", err)
		return nil, err
	}

	lenbuffer := make([]byte, 2)
	_, err = conn.Read(lenbuffer)

	if err != nil {
		sender.logEntry.Error(conn.RemoteAddr().String(), " connection error: ", err)
		return nil, err
	}

	bytelen := decodeLen(lenbuffer)

	buffer := make([]byte, bytelen)
	_, err2 := conn.Read(buffer)
	sender.logEntry.Debugf("Socket Sender Receive data : %s", hex.EncodeToString(buffer))

	if err2 != nil {
		sender.logEntry.Error(conn.RemoteAddr().String(), " connection error: ", err)
		return nil, err
	}
	return buffer, nil
}

func (sender *SocketSender) initConnectionPool() error {
	factory := func() (net.Conn, error) { return net.Dial("tcp", sender.Host+":"+sender.Port) }
	p, err := pool.NewPool(sender.MinConn, sender.MaxConn, factory)
	if err != nil {
		return err
	}
	sender.connpool = p
	return nil
}

func decodeLen(bytes []byte) uint16 {
	return binary.BigEndian.Uint16(bytes)
}
<|endoftext|>


<commit_before>package web

import (
	"net/http"
	"net/url"
	"time"

	"github.com/pkg/errors"
	"github.com/yuuki/diamondb/lib/config"
	"github.com/yuuki/diamondb/lib/env"
	"github.com/yuuki/diamondb/lib/log"
	"github.com/yuuki/diamondb/lib/query"
	"github.com/yuuki/diamondb/lib/timeparser"
)

const (
	// DayTime is one day period.
	DayTime = time.Duration(24*60*60) * time.Second
)

// PingHandler returns a HTTP handler for the endpoint to ping storage.
func PingHandler(env *env.Env) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if err := env.Fetcher.Ping(); err != nil {
			unavaliableError(w, errors.Cause(err).Error())
			return
		}
		ok(w, "PONG")
		return
	})
}

// InspectHandler returns a HTTP handler for the endpoint to inspect information.
func InspectHandler(env *env.Env) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		renderJSON(w, http.StatusOK, config.Config)
		return
	})
}

// RenderHandler returns a HTTP handler for the endpoint to read data.
func RenderHandler(env *env.Env) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		until := time.Now().Round(time.Second)
		from := until.Add(-DayTime)

		if v := r.FormValue("from"); v != "" {
			t, err := timeparser.ParseAtTime(url.QueryEscape(v))
			if err != nil {
				log.Printf("%+v", err) // Print stack trace by pkg/errors
				badRequest(w, errors.Cause(err).Error())
				return
			}
			from = t
		}
		if v := r.FormValue("until"); v != "" {
			t, err := timeparser.ParseAtTime(url.QueryEscape(v))
			if err != nil {
				log.Printf("%+v", err) // Print stack trace by pkg/errors
				badRequest(w, errors.Cause(err).Error())
				return
			}
			until = t
		}
		log.Debugf("from:%d until:%d", from.Unix(), until.Unix())

		targets := r.Form["target"]
		if len(targets) < 1 {
			badRequest(w, "no targets requested")
			return
		}

		seriesSlice, err := query.EvalTargets(env.Fetcher, targets, from, until)
		if err != nil {
			log.Printf("%+v", err) // Print stack trace by pkg/errors
			switch err.(type) {
			case *query.ParserError, *query.UnsupportedFunctionError:
				badRequest(w, errors.Cause(err).Error())
			default:
				serverError(w, errors.Cause(err).Error())
			}
			return
		}
		renderJSON(w, http.StatusOK, seriesSlice)
	})
}
<commit_msg>Tweak<commit_after>package web

import (
	"net/http"
	"net/url"
	"time"

	"github.com/pkg/errors"

	"github.com/yuuki/diamondb/lib/config"
	"github.com/yuuki/diamondb/lib/env"
	"github.com/yuuki/diamondb/lib/log"
	"github.com/yuuki/diamondb/lib/query"
	"github.com/yuuki/diamondb/lib/timeparser"
)

const (
	// DayTime is one day period.
	DayTime = time.Duration(24*60*60) * time.Second
)

// PingHandler returns a HTTP handler for the endpoint to ping storage.
func PingHandler(env *env.Env) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if err := env.Fetcher.Ping(); err != nil {
			unavaliableError(w, errors.Cause(err).Error())
			return
		}
		ok(w, "PONG")
		return
	})
}

// InspectHandler returns a HTTP handler for the endpoint to inspect information.
func InspectHandler(env *env.Env) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		renderJSON(w, http.StatusOK, config.Config)
		return
	})
}

// RenderHandler returns a HTTP handler for the endpoint to read data.
func RenderHandler(env *env.Env) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		until := time.Now().Round(time.Second)
		from := until.Add(-DayTime)

		if v := r.FormValue("from"); v != "" {
			t, err := timeparser.ParseAtTime(url.QueryEscape(v))
			if err != nil {
				log.Printf("%+v", err) // Print stack trace by pkg/errors
				badRequest(w, errors.Cause(err).Error())
				return
			}
			from = t
		}
		if v := r.FormValue("until"); v != "" {
			t, err := timeparser.ParseAtTime(url.QueryEscape(v))
			if err != nil {
				log.Printf("%+v", err) // Print stack trace by pkg/errors
				badRequest(w, errors.Cause(err).Error())
				return
			}
			until = t
		}
		log.Debugf("from:%d until:%d", from.Unix(), until.Unix())

		targets := r.Form["target"]
		if len(targets) < 1 {
			badRequest(w, "no targets requested")
			return
		}

		seriesSlice, err := query.EvalTargets(env.Fetcher, targets, from, until)
		if err != nil {
			log.Printf("%+v", err) // Print stack trace by pkg/errors
			switch err.(type) {
			case *query.ParserError, *query.UnsupportedFunctionError:
				badRequest(w, errors.Cause(err).Error())
			default:
				serverError(w, errors.Cause(err).Error())
			}
			return
		}
		renderJSON(w, http.StatusOK, seriesSlice)
	})
}
<|endoftext|>


<commit_before>package lntest

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sync"

	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)

const (
	// feeServiceTarget is the confirmation target for which a fee estimate
	// is returned. Requests for higher confirmation targets will fall back
	// to this.
	feeServiceTarget = 2
)

// feeService runs a web service that provides fee estimation information.
type feeService struct {
	feeEstimates

	srv *http.Server
	wg  sync.WaitGroup

	url string

	lock sync.Mutex
}

// feeEstimates contains the current fee estimates.
type feeEstimates struct {
	Fees map[uint32]uint32 `json:"fee_by_block_target"`
}

// startFeeService spins up a go-routine to serve fee estimates.
func startFeeService() *feeService {
	port := NextAvailablePort()
	f := feeService{
		url: fmt.Sprintf("http://localhost:%v/fee-estimates.json", port),
	}

	// Initialize default fee estimate.
	f.Fees = map[uint32]uint32{feeServiceTarget: 50000}

	listenAddr := fmt.Sprintf(":%v", port)
	f.srv = &http.Server{
		Addr: listenAddr,
	}

	http.HandleFunc("/fee-estimates.json", f.handleRequest)

	f.wg.Add(1)
	go func() {
		defer f.wg.Done()

		if err := f.srv.ListenAndServe(); err != http.ErrServerClosed {
			fmt.Printf("error: cannot start fee api: %v", err)
		}
	}()

	return &f
}

// handleRequest handles a client request for fee estimates.
func (f *feeService) handleRequest(w http.ResponseWriter, r *http.Request) {
	f.lock.Lock()
	defer f.lock.Unlock()

	bytes, err := json.Marshal(f.feeEstimates)
	if err != nil {
		fmt.Printf("error: cannot serialize "+
			"estimates: %v", err)

		return
	}

	_, err = io.WriteString(w, string(bytes))
	if err != nil {
		fmt.Printf("error: cannot send estimates: %v",
			err)
	}
}

// stop stops the web server.
func (f *feeService) stop() {
	if err := f.srv.Shutdown(context.Background()); err != nil {
		fmt.Printf("error: cannot stop fee api: %v", err)
	}

	f.wg.Wait()
}

// setFee changes the current fee estimate for the fixed confirmation target.
func (f *feeService) setFee(fee chainfee.SatPerKWeight) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.Fees[feeServiceTarget] = uint32(fee.FeePerKVByte())
}

// setFeeWithConf sets a fee for the given confirmation target.
func (f *feeService) setFeeWithConf(fee chainfee.SatPerKWeight, conf uint32) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.Fees[conf] = uint32(fee.FeePerKVByte())
}
<commit_msg>itest: change feeServiceTarget to be 1<commit_after>package lntest

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sync"

	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
)

const (
	// feeServiceTarget is the confirmation target for which a fee estimate
	// is returned. Requests for higher confirmation targets will fall back
	// to this.
	feeServiceTarget = 1
)

// feeService runs a web service that provides fee estimation information.
type feeService struct {
	feeEstimates

	srv *http.Server
	wg  sync.WaitGroup

	url string

	lock sync.Mutex
}

// feeEstimates contains the current fee estimates.
type feeEstimates struct {
	Fees map[uint32]uint32 `json:"fee_by_block_target"`
}

// startFeeService spins up a go-routine to serve fee estimates.
func startFeeService() *feeService {
	port := NextAvailablePort()
	f := feeService{
		url: fmt.Sprintf("http://localhost:%v/fee-estimates.json", port),
	}

	// Initialize default fee estimate.
	f.Fees = map[uint32]uint32{feeServiceTarget: 50000}

	listenAddr := fmt.Sprintf(":%v", port)
	f.srv = &http.Server{
		Addr: listenAddr,
	}

	http.HandleFunc("/fee-estimates.json", f.handleRequest)

	f.wg.Add(1)
	go func() {
		defer f.wg.Done()

		if err := f.srv.ListenAndServe(); err != http.ErrServerClosed {
			fmt.Printf("error: cannot start fee api: %v", err)
		}
	}()

	return &f
}

// handleRequest handles a client request for fee estimates.
func (f *feeService) handleRequest(w http.ResponseWriter, r *http.Request) {
	f.lock.Lock()
	defer f.lock.Unlock()

	bytes, err := json.Marshal(f.feeEstimates)
	if err != nil {
		fmt.Printf("error: cannot serialize "+
			"estimates: %v", err)

		return
	}

	_, err = io.WriteString(w, string(bytes))
	if err != nil {
		fmt.Printf("error: cannot send estimates: %v",
			err)
	}
}

// stop stops the web server.
func (f *feeService) stop() {
	if err := f.srv.Shutdown(context.Background()); err != nil {
		fmt.Printf("error: cannot stop fee api: %v", err)
	}

	f.wg.Wait()
}

// setFee changes the current fee estimate for the fixed confirmation target.
func (f *feeService) setFee(fee chainfee.SatPerKWeight) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.Fees[feeServiceTarget] = uint32(fee.FeePerKVByte())
}

// setFeeWithConf sets a fee for the given confirmation target.
func (f *feeService) setFeeWithConf(fee chainfee.SatPerKWeight, conf uint32) {
	f.lock.Lock()
	defer f.lock.Unlock()

	f.Fees[conf] = uint32(fee.FeePerKVByte())
}
<|endoftext|>