<commit_before>/**
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * 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 cmds

import (
	"net/http"
	"strings"

	"fmt"
	"github.com/fabric8io/gofabric8/client"
	"github.com/fabric8io/gofabric8/util"
	oclient "github.com/openshift/origin/pkg/client"
	"github.com/spf13/cobra"
	"k8s.io/kubernetes/pkg/api"
	k8sclient "k8s.io/kubernetes/pkg/client/unversioned"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util/sets"
)

type validateFunc func(c *k8sclient.Client, f *cmdutil.Factory) (Result, error)
type oValidateFunc func(c *oclient.Client, f *cmdutil.Factory) (Result, error)

func NewCmdValidate(f *cmdutil.Factory) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "validate",
		Short: "Validate your Kubernetes or OpenShift environment",
		Long:  `validate your Kubernetes or OpenShift environment`,
		PreRun: func(cmd *cobra.Command, args []string) {
			showBanner()
		},
		Run: func(cmd *cobra.Command, args []string) {
			c, cfg := client.NewClient(f)
			ns, _, _ := f.DefaultNamespace()
			util.Info("Validating your ")
			util.Success(string(util.TypeOfMaster(c)))
			util.Info(" installation at ")
			util.Success(cfg.Host)
			util.Info(" in namespace ")
			util.Successf("%s\n\n", ns)
			printValidationResult("Service account", validateServiceAccount, c, f)
			printValidationResult("Console", validateConsoleDeployment, c, f)

			r, err := validateProxyServiceRestAPI(c, f, cfg.Host)
			printResult("REST Proxy Service API", r, err)

			if util.TypeOfMaster(c) == util.Kubernetes {
				printValidationResult("Jenkinshift Service", validateJenkinshiftService, c, f)
			}

			if util.TypeOfMaster(c) == util.OpenShift {
				printValidationResult("Router", validateRouter, c, f)
				oc, _ := client.NewOpenShiftClient(cfg)
				printOValidationResult("Templates", validateTemplates, oc, f)
				printValidationResult("SecurityContextConstraints", validateSecurityContextConstraints, c, f)
			}

			printValidationResult("PersistentVolumeClaims", validatePersistenceVolumeClaims, c, f)
			printValidationResult("ConfigMaps", validateConfigMaps, c, f)
		},
	}

	return cmd
}

func printValidationResult(check string, v validateFunc, c *k8sclient.Client, f *cmdutil.Factory) {
	r, err := v(c, f)
	printResult(check, r, err)
}

func printOValidationResult(check string, v oValidateFunc, c *oclient.Client, f *cmdutil.Factory) {
	r, err := v(c, f)
	printResult(check, r, err)
}

func printError(check string, err error) {
	r := Success
	if err != nil {
		r = Failure
	}
	printResult(check, r, err)
}

func printResult(check string, r Result, err error) {
	if err != nil {
		r = Failure
	}
	padLen := 78 - len(check)
	pad := ""
	if padLen > 0 {
		pad = strings.Repeat(".", padLen)
	}
	util.Infof("%s%s", check, pad)
	if r == Failure {
		util.Failuref("%-2s", r)
	} else {
		util.Successf("%-2s", r)
	}
	if err != nil {
		util.Failuref("%v", err)
	}
	util.Blank()
}

func validateServiceAccount(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	sa, err := c.ServiceAccounts(ns).Get("fabric8")
	if sa != nil {
		return Success, err
	}
	return Failure, err
}

func validateConsoleDeployment(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	rc, err := c.ReplicationControllers(ns).Get("fabric8")
	if rc != nil {
		return Success, err
	}
	return Failure, err
}

func validateProxyServiceRestAPI(c *k8sclient.Client, f *cmdutil.Factory, host string) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	uri := host + "/api/v1/proxy/namespaces/" + ns + "/services/fabric8/"

	resp, err := http.Get(uri)
	if err != nil {
		err = fmt.Errorf("Cannot query the API Server REST Proxy Service API at %s. Can the master node see the service IPs? Got error: %v", uri, err)
		return Failure, err
	}
	defer resp.Body.Close()

	statusCode := resp.StatusCode
	if statusCode < 200 || statusCode >= 300 {
		err = fmt.Errorf("Cannot query the API Server REST Proxy Service API at %s. Can the master node see the service IPs? Got status code: %d", uri, statusCode)
		return Failure, err
	}
	return Success, err
}

func validatePersistenceVolumeClaims(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	rc, err := c.PersistentVolumeClaims(ns).List(api.ListOptions{})
	if err != nil {
		util.Fatalf("Failed to get PersistentVolumeClaims, %s in namespace %s\n", err, ns)
	}
	if rc != nil {
		items := rc.Items
		pendingClaimNames := make([]string, 0, len(items))
		for _, item := range items {
			status := item.Status.Phase
			if status != "Bound" {
				pendingClaimNames = append(pendingClaimNames, item.ObjectMeta.Name)
			}
		}
		if len(pendingClaimNames) > 0 {
			util.Failuref("PersistentVolumeClaim not Bound for: %s. You need to create a PersistentVolume!\n", strings.Join(pendingClaimNames, ", "))
			util.Info(`
to generate a single node PersistentVolume then type something like this:

cat <<EOF | oc create -f -
---
kind: PersistentVolume
apiVersion: v1
metadata:
  name: fabric8
spec:
  accessModes:
    - ReadWrite
  capacity:
    storage: 1000
  hostPath:
    path: /opt/fabric8-data
EOF

`)
			return Failure, err
		}
		return Success, err
	}
	return Failure, err
}

func validateRouter(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	requirement, err := labels.NewRequirement("router", labels.EqualsOperator, sets.NewString("router"))
	if err != nil {
		return Failure, err
	}
	label := labels.NewSelector().Add(*requirement)

	rc, err := c.ReplicationControllers(ns).List(api.ListOptions{LabelSelector: label})
	if err != nil {
		util.Fatalf("Failed to get PersistentVolumeClaims, %s in namespace %s\n", err, ns)
	}
	if rc != nil {
		items := rc.Items
		if len(items) > 0 {
			return Success, err
		}
	}
	//util.Fatalf("No router running in namespace %s\n", ns)
	// TODO lets create a router
	return Failure, err
}

func validateSecurityContextConstraints(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	name := Fabric8SCC
	if ns != "default" {
		name += "-" + ns
	}
	rc, err := c.SecurityContextConstraints().Get(name)
	if err != nil {
		util.Fatalf("Failed to get SecurityContextConstraints, %s in namespace %s\n", err, ns)
	}
	if rc != nil {
		return Success, err
	}
	return Failure, err
}

func validateJenkinshiftService(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	svc, err := c.Services(ns).Get("jenkinshift")
	if svc != nil {
		return Success, err
	}
	return Failure, err
}

func validateTemplates(c *oclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	rc, err := c.Templates(ns).Get("management")
	if rc != nil {
		return Success, err
	}
	return Failure, err
}

func validateConfigMaps(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	list, err := c.ConfigMaps(ns).List(api.ListOptions{})
	if err == nil && list != nil {
		return Success, err
	}
	return Failure, err
}
<commit_msg>Use kube clients http client to validate REST proxy<commit_after>/**
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * 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 cmds

import (
	"strings"

	"fmt"

	"github.com/fabric8io/gofabric8/client"
	"github.com/fabric8io/gofabric8/util"
	oclient "github.com/openshift/origin/pkg/client"
	"github.com/spf13/cobra"
	"k8s.io/kubernetes/pkg/api"
	k8sclient "k8s.io/kubernetes/pkg/client/unversioned"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util/sets"
)

type validateFunc func(c *k8sclient.Client, f *cmdutil.Factory) (Result, error)
type oValidateFunc func(c *oclient.Client, f *cmdutil.Factory) (Result, error)

func NewCmdValidate(f *cmdutil.Factory) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "validate",
		Short: "Validate your Kubernetes or OpenShift environment",
		Long:  `validate your Kubernetes or OpenShift environment`,
		PreRun: func(cmd *cobra.Command, args []string) {
			showBanner()
		},
		Run: func(cmd *cobra.Command, args []string) {
			c, cfg := client.NewClient(f)
			ns, _, _ := f.DefaultNamespace()
			util.Info("Validating your ")
			util.Success(string(util.TypeOfMaster(c)))
			util.Info(" installation at ")
			util.Success(cfg.Host)
			util.Info(" in namespace ")
			util.Successf("%s\n\n", ns)
			printValidationResult("Service account", validateServiceAccount, c, f)
			printValidationResult("Console", validateConsoleDeployment, c, f)

			r, err := validateProxyServiceRestAPI(c, f, cfg.Host)
			printResult("REST Proxy Service API", r, err)

			if util.TypeOfMaster(c) == util.Kubernetes {
				printValidationResult("Jenkinshift Service", validateJenkinshiftService, c, f)
			}

			if util.TypeOfMaster(c) == util.OpenShift {
				printValidationResult("Router", validateRouter, c, f)
				oc, _ := client.NewOpenShiftClient(cfg)
				printOValidationResult("Templates", validateTemplates, oc, f)
				printValidationResult("SecurityContextConstraints", validateSecurityContextConstraints, c, f)
			}

			printValidationResult("PersistentVolumeClaims", validatePersistenceVolumeClaims, c, f)
			printValidationResult("ConfigMaps", validateConfigMaps, c, f)
		},
	}

	return cmd
}

func printValidationResult(check string, v validateFunc, c *k8sclient.Client, f *cmdutil.Factory) {
	r, err := v(c, f)
	printResult(check, r, err)
}

func printOValidationResult(check string, v oValidateFunc, c *oclient.Client, f *cmdutil.Factory) {
	r, err := v(c, f)
	printResult(check, r, err)
}

func printError(check string, err error) {
	r := Success
	if err != nil {
		r = Failure
	}
	printResult(check, r, err)
}

func printResult(check string, r Result, err error) {
	if err != nil {
		r = Failure
	}
	padLen := 78 - len(check)
	pad := ""
	if padLen > 0 {
		pad = strings.Repeat(".", padLen)
	}
	util.Infof("%s%s", check, pad)
	if r == Failure {
		util.Failuref("%-2s", r)
	} else {
		util.Successf("%-2s", r)
	}
	if err != nil {
		util.Failuref("%v", err)
	}
	util.Blank()
}

func validateServiceAccount(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	sa, err := c.ServiceAccounts(ns).Get("fabric8")
	if sa != nil {
		return Success, err
	}
	return Failure, err
}

func validateConsoleDeployment(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	rc, err := c.ReplicationControllers(ns).Get("fabric8")
	if rc != nil {
		return Success, err
	}
	return Failure, err
}

func validateProxyServiceRestAPI(c *k8sclient.Client, f *cmdutil.Factory, host string) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	uri := host + "/api/v1/proxy/namespaces/" + ns + "/services/fabric8/"

	resp, err := c.Client.Get(uri)
	if err != nil {
		err = fmt.Errorf("Cannot query the API Server REST Proxy Service API at %s. Can the master node see the service IPs? Got error: %v", uri, err)
		return Failure, err
	}
	defer resp.Body.Close()

	statusCode := resp.StatusCode
	if statusCode < 200 || statusCode >= 300 {
		err = fmt.Errorf("Cannot query the API Server REST Proxy Service API at %s. Can the master node see the service IPs? Got status code: %d", uri, statusCode)
		return Failure, err
	}
	return Success, err
}

func validatePersistenceVolumeClaims(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	rc, err := c.PersistentVolumeClaims(ns).List(api.ListOptions{})
	if err != nil {
		util.Fatalf("Failed to get PersistentVolumeClaims, %s in namespace %s\n", err, ns)
	}
	if rc != nil {
		items := rc.Items
		pendingClaimNames := make([]string, 0, len(items))
		for _, item := range items {
			status := item.Status.Phase
			if status != "Bound" {
				pendingClaimNames = append(pendingClaimNames, item.ObjectMeta.Name)
			}
		}
		if len(pendingClaimNames) > 0 {
			util.Failuref("PersistentVolumeClaim not Bound for: %s. You need to create a PersistentVolume!\n", strings.Join(pendingClaimNames, ", "))
			util.Info(`
to generate a single node PersistentVolume then type something like this:

cat <<EOF | oc create -f -
---
kind: PersistentVolume
apiVersion: v1
metadata:
  name: fabric8
spec:
  accessModes:
    - ReadWrite
  capacity:
    storage: 1000
  hostPath:
    path: /opt/fabric8-data
EOF

`)
			return Failure, err
		}
		return Success, err
	}
	return Failure, err
}

func validateRouter(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	requirement, err := labels.NewRequirement("router", labels.EqualsOperator, sets.NewString("router"))
	if err != nil {
		return Failure, err
	}
	label := labels.NewSelector().Add(*requirement)

	rc, err := c.ReplicationControllers(ns).List(api.ListOptions{LabelSelector: label})
	if err != nil {
		util.Fatalf("Failed to get PersistentVolumeClaims, %s in namespace %s\n", err, ns)
	}
	if rc != nil {
		items := rc.Items
		if len(items) > 0 {
			return Success, err
		}
	}
	//util.Fatalf("No router running in namespace %s\n", ns)
	// TODO lets create a router
	return Failure, err
}

func validateSecurityContextConstraints(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	name := Fabric8SCC
	if ns != "default" {
		name += "-" + ns
	}
	rc, err := c.SecurityContextConstraints().Get(name)
	if err != nil {
		util.Fatalf("Failed to get SecurityContextConstraints, %s in namespace %s\n", err, ns)
	}
	if rc != nil {
		return Success, err
	}
	return Failure, err
}

func validateJenkinshiftService(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	svc, err := c.Services(ns).Get("jenkinshift")
	if svc != nil {
		return Success, err
	}
	return Failure, err
}

func validateTemplates(c *oclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	rc, err := c.Templates(ns).Get("management")
	if rc != nil {
		return Success, err
	}
	return Failure, err
}

func validateConfigMaps(c *k8sclient.Client, f *cmdutil.Factory) (Result, error) {
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	list, err := c.ConfigMaps(ns).List(api.ListOptions{})
	if err == nil && list != nil {
		return Success, err
	}
	return Failure, err
}
<|endoftext|>


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

// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package column

import (
	"strings"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/model"
	"github.com/pingcap/tidb/mysql"
	"github.com/pingcap/tidb/util/types"
)

// Col provides meta data describing a table column.
type Col struct {
	model.ColumnInfo
}

// PrimaryKeyName defines primary key name.
const PrimaryKeyName = "PRIMARY"

// IndexedCol defines an index with info.
type IndexedCol struct {
	model.IndexInfo
	X kv.Index
}

// String implements fmt.Stringer interface.
func (c *Col) String() string {
	ans := []string{c.Name.O, types.TypeToStr(c.Tp, c.Charset)}
	if mysql.HasAutoIncrementFlag(c.Flag) {
		ans = append(ans, "AUTO_INCREMENT")
	}
	if mysql.HasNotNullFlag(c.Flag) {
		ans = append(ans, "NOT NULL")
	}
	return strings.Join(ans, " ")
}

// FindCol finds column in cols by name.
func FindCol(cols []*Col, name string) (c *Col) {
	for _, c := range cols {
		if strings.EqualFold(c.Name.O, name) {
			return c
		}
	}
	return nil
}

// FindCols finds columns in cols by names.
func FindCols(cols []*Col, names []string) ([]*Col, error) {
	var rcols []*Col
	for _, name := range names {
		col := FindCol(cols, name)
		if col != nil {
			rcols = append(rcols, col)
		} else {
			return nil, errors.Errorf("unknown column %s", name)
		}
	}

	return rcols, nil
}

// FindOnUpdateCols finds columns which have OnUpdateNow flag.
func FindOnUpdateCols(cols []*Col) []*Col {
	var rcols []*Col
	for _, c := range cols {
		if mysql.HasOnUpdateNowFlag(c.Flag) {
			rcols = append(rcols, c)
		}
	}

	return rcols
}

// CastValues casts values based on columns type.
func CastValues(ctx context.Context, rec []interface{}, cols []*Col) (err error) {
	for _, c := range cols {
		rec[c.Offset], err = types.Convert(rec[c.Offset], &c.FieldType)
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

// ColDesc describes column information like MySQL desc and show columns do.
type ColDesc struct {
	Field        string
	Type         string
	Collation    string
	Null         string
	Key          string
	DefaultValue interface{}
	Extra        string
	Privileges   string
	Comment      string
}

const defaultPrivileges string = "select,insert,update,references"

// GetTypeDesc gets the description for column type.
func (c *Col) GetTypeDesc() string {
	desc := c.FieldType.CompactStr()
	if mysql.HasUnsignedFlag(c.Flag) {
		desc += " UNSIGNED"
	}
	return desc
}

// NewColDesc returns a new ColDesc for a column.
func NewColDesc(col *Col) *ColDesc {
	// TODO: if we have no primary key and a unique index which's columns are all not null
	// we will set these columns' flag as PriKeyFlag
	// see https://dev.mysql.com/doc/refman/5.7/en/show-columns.html
	// create table
	name := col.Name
	nullFlag := "YES"
	if mysql.HasNotNullFlag(col.Flag) {
		nullFlag = "NO"
	}
	keyFlag := ""
	if mysql.HasPriKeyFlag(col.Flag) {
		keyFlag = "PRI"
	} else if mysql.HasUniKeyFlag(col.Flag) {
		keyFlag = "UNI"
	} else if mysql.HasMultipleKeyFlag(col.Flag) {
		keyFlag = "MUL"
	}
	var defaultValue interface{}
	if !mysql.HasNoDefaultValueFlag(col.Flag) {
		defaultValue = col.DefaultValue
	}

	extra := ""
	if mysql.HasAutoIncrementFlag(col.Flag) {
		extra = "auto_increment"
	} else if mysql.HasOnUpdateNowFlag(col.Flag) {
		extra = "on update CURRENT_TIMESTAMP"
	}

	return &ColDesc{
		Field:        name.O,
		Type:         col.GetTypeDesc(),
		Collation:    col.Collate,
		Null:         nullFlag,
		Key:          keyFlag,
		DefaultValue: defaultValue,
		Extra:        extra,
		Privileges:   defaultPrivileges,
		Comment:      "",
	}
}

// ColDescFieldNames returns the fields name in result set for desc and show columns.
func ColDescFieldNames(full bool) []string {
	if full {
		return []string{"Field", "Type", "Collation", "Null", "Key", "Default", "Extra", "Privileges", "Comment"}
	}
	return []string{"Field", "Type", "Null", "Key", "Default", "Extra"}
}

// CheckOnce checks if there are duplicated column names in cols.
func CheckOnce(cols []*Col) error {
	m := map[string]struct{}{}
	for _, v := range cols {
		name := v.Name
		_, ok := m[name.L]
		if ok {
			return errors.Errorf("column specified twice - %s", name)
		}

		m[name.L] = struct{}{}
	}

	return nil
}

// CheckNotNull checks if nil value set to a column with NotNull flag is set.
func (c *Col) CheckNotNull(data interface{}) error {
	if mysql.HasNotNullFlag(c.Flag) && data == nil {
		return errors.Errorf("Column %s can't be null.", c.Name)
	}
	return nil
}

// CheckNotNull checks if row has nil value set to a column with NotNull flag set.
func CheckNotNull(cols []*Col, row []interface{}) error {
	for _, c := range cols {
		if err := c.CheckNotNull(row[c.Offset]); err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

// FetchValues fetches indexed values from a row.
func (idx *IndexedCol) FetchValues(r []interface{}) ([]interface{}, error) {
	var vals []interface{}
	for _, ic := range idx.Columns {
		if ic.Offset < 0 || ic.Offset > len(r) {
			return nil, errors.New("Index column offset out of bound")
		}
		vals = append(vals, r[ic.Offset])
	}
	return vals, nil
}
<commit_msg>Return *Col instead c *Col<commit_after>// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.

// Copyright 2015 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package column

import (
	"strings"

	"github.com/juju/errors"
	"github.com/pingcap/tidb/context"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/model"
	"github.com/pingcap/tidb/mysql"
	"github.com/pingcap/tidb/util/types"
)

// Col provides meta data describing a table column.
type Col struct {
	model.ColumnInfo
}

// PrimaryKeyName defines primary key name.
const PrimaryKeyName = "PRIMARY"

// IndexedCol defines an index with info.
type IndexedCol struct {
	model.IndexInfo
	X kv.Index
}

// String implements fmt.Stringer interface.
func (c *Col) String() string {
	ans := []string{c.Name.O, types.TypeToStr(c.Tp, c.Charset)}
	if mysql.HasAutoIncrementFlag(c.Flag) {
		ans = append(ans, "AUTO_INCREMENT")
	}
	if mysql.HasNotNullFlag(c.Flag) {
		ans = append(ans, "NOT NULL")
	}
	return strings.Join(ans, " ")
}

// FindCol finds column in cols by name.
func FindCol(cols []*Col, name string) *Col {
	for _, c := range cols {
		if strings.EqualFold(c.Name.O, name) {
			return c
		}
	}
	return nil
}

// FindCols finds columns in cols by names.
func FindCols(cols []*Col, names []string) ([]*Col, error) {
	var rcols []*Col
	for _, name := range names {
		col := FindCol(cols, name)
		if col != nil {
			rcols = append(rcols, col)
		} else {
			return nil, errors.Errorf("unknown column %s", name)
		}
	}

	return rcols, nil
}

// FindOnUpdateCols finds columns which have OnUpdateNow flag.
func FindOnUpdateCols(cols []*Col) []*Col {
	var rcols []*Col
	for _, c := range cols {
		if mysql.HasOnUpdateNowFlag(c.Flag) {
			rcols = append(rcols, c)
		}
	}

	return rcols
}

// CastValues casts values based on columns type.
func CastValues(ctx context.Context, rec []interface{}, cols []*Col) (err error) {
	for _, c := range cols {
		rec[c.Offset], err = types.Convert(rec[c.Offset], &c.FieldType)
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

// ColDesc describes column information like MySQL desc and show columns do.
type ColDesc struct {
	Field        string
	Type         string
	Collation    string
	Null         string
	Key          string
	DefaultValue interface{}
	Extra        string
	Privileges   string
	Comment      string
}

const defaultPrivileges string = "select,insert,update,references"

// GetTypeDesc gets the description for column type.
func (c *Col) GetTypeDesc() string {
	desc := c.FieldType.CompactStr()
	if mysql.HasUnsignedFlag(c.Flag) {
		desc += " UNSIGNED"
	}
	return desc
}

// NewColDesc returns a new ColDesc for a column.
func NewColDesc(col *Col) *ColDesc {
	// TODO: if we have no primary key and a unique index which's columns are all not null
	// we will set these columns' flag as PriKeyFlag
	// see https://dev.mysql.com/doc/refman/5.7/en/show-columns.html
	// create table
	name := col.Name
	nullFlag := "YES"
	if mysql.HasNotNullFlag(col.Flag) {
		nullFlag = "NO"
	}
	keyFlag := ""
	if mysql.HasPriKeyFlag(col.Flag) {
		keyFlag = "PRI"
	} else if mysql.HasUniKeyFlag(col.Flag) {
		keyFlag = "UNI"
	} else if mysql.HasMultipleKeyFlag(col.Flag) {
		keyFlag = "MUL"
	}
	var defaultValue interface{}
	if !mysql.HasNoDefaultValueFlag(col.Flag) {
		defaultValue = col.DefaultValue
	}

	extra := ""
	if mysql.HasAutoIncrementFlag(col.Flag) {
		extra = "auto_increment"
	} else if mysql.HasOnUpdateNowFlag(col.Flag) {
		extra = "on update CURRENT_TIMESTAMP"
	}

	return &ColDesc{
		Field:        name.O,
		Type:         col.GetTypeDesc(),
		Collation:    col.Collate,
		Null:         nullFlag,
		Key:          keyFlag,
		DefaultValue: defaultValue,
		Extra:        extra,
		Privileges:   defaultPrivileges,
		Comment:      "",
	}
}

// ColDescFieldNames returns the fields name in result set for desc and show columns.
func ColDescFieldNames(full bool) []string {
	if full {
		return []string{"Field", "Type", "Collation", "Null", "Key", "Default", "Extra", "Privileges", "Comment"}
	}
	return []string{"Field", "Type", "Null", "Key", "Default", "Extra"}
}

// CheckOnce checks if there are duplicated column names in cols.
func CheckOnce(cols []*Col) error {
	m := map[string]struct{}{}
	for _, v := range cols {
		name := v.Name
		_, ok := m[name.L]
		if ok {
			return errors.Errorf("column specified twice - %s", name)
		}

		m[name.L] = struct{}{}
	}

	return nil
}

// CheckNotNull checks if nil value set to a column with NotNull flag is set.
func (c *Col) CheckNotNull(data interface{}) error {
	if mysql.HasNotNullFlag(c.Flag) && data == nil {
		return errors.Errorf("Column %s can't be null.", c.Name)
	}
	return nil
}

// CheckNotNull checks if row has nil value set to a column with NotNull flag set.
func CheckNotNull(cols []*Col, row []interface{}) error {
	for _, c := range cols {
		if err := c.CheckNotNull(row[c.Offset]); err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

// FetchValues fetches indexed values from a row.
func (idx *IndexedCol) FetchValues(r []interface{}) ([]interface{}, error) {
	var vals []interface{}
	for _, ic := range idx.Columns {
		if ic.Offset < 0 || ic.Offset > len(r) {
			return nil, errors.New("Index column offset out of bound")
		}
		vals = append(vals, r[ic.Offset])
	}
	return vals, nil
}
<|endoftext|>


<commit_before>package command

import (
	"bufio"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"

	"github.com/go-almeria/gitx/api"
	"github.com/go-almeria/gitx/meta"
)

// CountCommand Outputs commit count
type CountCommand struct {
	meta.Meta
}

func (c *CountCommand) Run(args []string) int {
	var all bool

	flags := c.Meta.FlagSet("count", meta.FlagSetDefault)
	flags.BoolVar(&all, "all", false, "")
	flags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()
	if len(args) > 1 {
		flags.Usage()
		c.Ui.Error(fmt.Sprintf("\ncount expects at most one argument"))
		return 1
	}

	g := *api.NewGit("shortlog HEAD -n -s")
	if err := g.Run(); err != nil {
		c.Ui.Error(fmt.Sprintf("%s", string(g.Err.Bytes())))
		return 1
	}

	re := regexp.MustCompile(`(\d+)\s+(\S+.*)`)
	scanner := bufio.NewScanner(&g.Out)

	count := 0
	for scanner.Scan() {
		results := re.FindAllStringSubmatch(scanner.Text(), -1)
		userCount, _ := strconv.Atoi(results[0][1])
		count += userCount
		user := results[0][2]
		if all {
			c.Ui.Info(fmt.Sprintf("%s (%d)", user, userCount))
		}
	}

	if err := scanner.Err(); err != nil {
		log.Fatal("--->", err)
	}
	c.Ui.Info(fmt.Sprintf("\ntotal %d", count))

	return 0
}

func (c *CountCommand) Synopsis() string {
	return "Outputs commit counts"
}

func (c *CountCommand) Help() string {
	helpText := `
Usage: gitx count [--all]
`
	return strings.TrimSpace(helpText)
}
<commit_msg>minor<commit_after>package command

import (
	"bufio"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/go-almeria/gitx/api"
	"github.com/go-almeria/gitx/meta"
)

// CountCommand Outputs commit count
type CountCommand struct {
	meta.Meta
}

func (c *CountCommand) Run(args []string) int {
	var all bool

	flags := c.Meta.FlagSet("count", meta.FlagSetDefault)
	flags.BoolVar(&all, "all", false, "")
	flags.Usage = func() { c.Ui.Error(c.Help()) }
	if err := flags.Parse(args); err != nil {
		return 1
	}

	args = flags.Args()
	if len(args) > 1 {
		flags.Usage()
		c.Ui.Error(fmt.Sprintf("\ncount expects at most one argument"))
		return 1
	}

	g := *api.NewGit("shortlog HEAD -n -s")
	if err := g.Run(); err != nil {
		c.Ui.Error(fmt.Sprintf("%s", string(g.Err.Bytes())))
		return 1
	}

	re := regexp.MustCompile(`(\d+)\s+(\S+.*)`)
	scanner := bufio.NewScanner(&g.Out)

	count := 0
	for scanner.Scan() {
		results := re.FindAllStringSubmatch(scanner.Text(), -1)
		userCount, _ := strconv.Atoi(results[0][1])
		count += userCount
		user := results[0][2]
		if all {
			c.Ui.Info(fmt.Sprintf("%s (%d)", user, userCount))
		}
	}

	if err := scanner.Err(); err != nil {
		c.Ui.Error(fmt.Sprintf("%v", err))
	}

	c.Ui.Info(fmt.Sprintf("\ntotal %d", count))

	return 0
}

func (c *CountCommand) Synopsis() string {
	return "Outputs commit counts"
}

func (c *CountCommand) Help() string {
	helpText := `
Usage: gitx count [--all]
`
	return strings.TrimSpace(helpText)
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/hashicorp/vault/api"
	"github.com/posener/complete"
)

// LoginHandler is the interface that any auth handlers must implement to enable
// auth via the CLI.
type LoginHandler interface {
	Auth(*api.Client, map[string]string) (*api.Secret, error)
	Help() string
}

type LoginCommand struct {
	*BaseCommand

	Handlers map[string]LoginHandler

	flagMethod    string
	flagPath      string
	flagNoStore   bool
	flagTokenOnly bool

	// Deprecations
	// TODO: remove in 0.9.0
	flagNoVerify bool

	testStdin io.Reader // for tests
}

func (c *LoginCommand) Synopsis() string {
	return "Authenticate locally"
}

func (c *LoginCommand) Help() string {
	helpText := `
Usage: vault login [options] [AUTH K=V...]

  Authenticates users or machines to Vault using the provided arguments. A
  successful authentication results in a Vault token - conceptually similar to
  a session token on a website. By default, this token is cached on the local
  machine for future requests.

  The default auth method is "token". If not supplied via the CLI,
  Vault will prompt for input. If the argument is "-", the values are read
  from stdin.

  The -method flag allows using other auth methods, such as userpass, github, or
  cert. For these, additional "K=V" pairs may be required. For example, to
  authenticate to the userpass auth method:

      $ vault login -method=userpass username=my-username

  For more information about the list of configuration parameters available for
  a given auth method, use the "vault auth help TYPE". You can also use "vault
  auth list" to see the list of enabled auth methods.

  If an auth method is enabled at a non-standard path, the -method flag still
  refers to the canonical type, but the -path flag refers to the enabled path.
  If a github auth method was enabled at "github-ent", authenticate like this:

      $ vault login -method=github -path=github-prod

  If the authentication is requested with response wrapping (via -wrap-ttl),
  the returned token is automatically unwrapped unless:

    - The -token-only flag is used, in which case this command will output
      the wrapping token.

    - The -no-store flag is used, in which case this command will output the
      details of the wrapping token.

` + c.Flags().Help()

	return strings.TrimSpace(helpText)
}

func (c *LoginCommand) Flags() *FlagSets {
	set := c.flagSet(FlagSetHTTP | FlagSetOutputField | FlagSetOutputFormat)

	f := set.NewFlagSet("Command Options")

	f.StringVar(&StringVar{
		Name:       "method",
		Target:     &c.flagMethod,
		Default:    "token",
		Completion: c.PredictVaultAvailableAuths(),
		Usage: "Type of authentication to use such as \"userpass\" or " +
			"\"ldap\". Note this corresponds to the TYPE, not the enabled path. " +
			"Use -path to specify the path where the authentication is enabled.",
	})

	f.StringVar(&StringVar{
		Name:       "path",
		Target:     &c.flagPath,
		Default:    "",
		Completion: c.PredictVaultAuths(),
		Usage: "Remote path in Vault where the auth method is enabled. " +
			"This defaults to the TYPE of method (e.g. userpass -> userpass/).",
	})

	f.BoolVar(&BoolVar{
		Name:    "no-store",
		Target:  &c.flagNoStore,
		Default: false,
		Usage: "Do not persist the token to the token helper (usually the " +
			"local filesystem) after authentication for use in future requests. " +
			"The token will only be displayed in the command output.",
	})

	f.BoolVar(&BoolVar{
		Name:    "token-only",
		Target:  &c.flagTokenOnly,
		Default: false,
		Usage: "Output only the token with no verification. This flag is a " +
			"shortcut for \"-field=token -no-store\". Setting those flags to other " +
			"values will have no affect.",
	})

	// Deprecations
	// TODO: remove in 0.9.0
	f.BoolVar(&BoolVar{
		Name:    "no-verify",
		Target:  &c.flagNoVerify,
		Hidden:  true,
		Default: false,
		Usage:   "",
	})

	return set
}

func (c *LoginCommand) AutocompleteArgs() complete.Predictor {
	return nil
}

func (c *LoginCommand) AutocompleteFlags() complete.Flags {
	return c.Flags().Completions()
}

func (c *LoginCommand) Run(args []string) int {
	f := c.Flags()

	if err := f.Parse(args); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	args = f.Args()

	// Deprecations
	// TODO: remove in 0.10.0
	switch {
	case c.flagNoVerify:
		c.UI.Warn(wrapAtLength(
			"WARNING! The -no-verify flag is deprecated. In the past, Vault " +
				"performed a lookup on a token after authentication. This is no " +
				"longer the case for all auth methods except \"token\". Vault will " +
				"still attempt to perform a lookup when given a token directly " +
				"because that is how it gets the list of policies, ttl, and other " +
				"metadata. To disable this lookup, specify \"lookup=false\" as a " +
				"configuration option to the token auth method, like this:"))
		c.UI.Warn("")
		c.UI.Warn("    $ vault auth token=ABCD lookup=false")
		c.UI.Warn("")
		c.UI.Warn("Or omit the token and Vault will prompt for it:")
		c.UI.Warn("")
		c.UI.Warn("    $ vault auth lookup=false")
		c.UI.Warn("    Token (will be hidden): ...")
		c.UI.Warn("")
		c.UI.Warn(wrapAtLength(
			"If you are not using token authentication, you can safely omit this " +
				"flag. Vault will not perform a lookup after authentication."))
		c.UI.Warn("")

		// There's no point in passing this to other auth handlers...
		if c.flagMethod == "token" {
			args = append(args, "lookup=false")
		}
	}

	// Set the right flags if the user requested token-only - this overrides
	// any previously configured values, as documented.
	if c.flagTokenOnly {
		c.flagNoStore = true
		c.flagField = "token"
	}

	// Get the auth method
	authMethod := sanitizePath(c.flagMethod)
	if authMethod == "" {
		authMethod = "token"
	}

	// If no path is specified, we default the path to the method type
	// or use the plugin name if it's a plugin
	authPath := c.flagPath
	if authPath == "" {
		authPath = ensureTrailingSlash(authMethod)
	}

	// Get the handler function
	authHandler, ok := c.Handlers[authMethod]
	if !ok {
		c.UI.Error(wrapAtLength(fmt.Sprintf(
			"Unknown auth method: %s. Use \"vault auth list\" to see the "+
				"complete list of auth methods. Additionally, some "+
				"auth methods are only available via the HTTP API.",
			authMethod)))
		return 1
	}

	// Pull our fake stdin if needed
	stdin := (io.Reader)(os.Stdin)
	if c.testStdin != nil {
		stdin = c.testStdin
	}

	// If the user provided a token, pass it along to the auth provider.
	if authMethod == "token" && len(args) > 0 && !strings.Contains(args[0], "=") {
		args = append([]string{"token=" + args[0]}, args[1:]...)
	}

	config, err := parseArgsDataString(stdin, args)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error parsing configuration: %s", err))
		return 1
	}

	// If the user did not specify a mount path, use the provided mount path.
	if config["mount"] == "" && authPath != "" {
		config["mount"] = authPath
	}

	// Create the client
	client, err := c.Client()
	if err != nil {
		c.UI.Error(err.Error())
		return 2
	}

	// Authenticate delegation to the auth handler
	secret, err := authHandler.Auth(client, config)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error authenticating: %s", err))
		return 2
	}

	// Unset any previous token wrapping functionality. If the original request
	// was for a wrapped token, we don't want future requests to be wrapped.
	client.SetWrappingLookupFunc(func(string, string) string { return "" })

	// Recursively extract the token, handling wrapping
	unwrap := !c.flagTokenOnly && !c.flagNoStore
	secret, isWrapped, err := c.extractToken(client, secret, unwrap)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error extracting token: %s", err))
		return 2
	}
	if secret == nil {
		c.UI.Error("Vault returned an empty secret")
		return 2
	}

	// Handle special cases if the token was wrapped
	if isWrapped {
		if c.flagTokenOnly {
			return PrintRawField(c.UI, secret, "wrapping_token")
		}
		if c.flagNoStore {
			return OutputSecret(c.UI, secret)
		}
	}

	// If we got this far, verify we have authentication data before continuing
	if secret.Auth == nil {
		c.UI.Error(wrapAtLength(
			"Vault returned a secret, but the secret has no authentication " +
				"information attached. This should never happen and is likely a " +
				"bug."))
		return 2
	}

	// Pull the token itself out, since we don't need the rest of the auth
	// information anymore/.
	token := secret.Auth.ClientToken

	if !c.flagNoStore {
		// Grab the token helper so we can store
		tokenHelper, err := c.TokenHelper()
		if err != nil {
			c.UI.Error(wrapAtLength(fmt.Sprintf(
				"Error initializing token helper. Please verify that the token "+
					"helper is available and properly configured for your system. The "+
					"error was: %s", err)))
			return 1
		}

		// Store the token in the local client
		if err := tokenHelper.Store(token); err != nil {
			c.UI.Error(fmt.Sprintf("Error storing token: %s", err))
			c.UI.Error(wrapAtLength(
				"Authentication was successful, but the token was not persisted. The "+
					"resulting token is shown below for your records.") + "\n")
			OutputSecret(c.UI, secret)
			return 2
		}

		// Warn if the VAULT_TOKEN environment variable is set, as that will take
		// precedence. We output as a warning, so piping should still work since it
		// will be on a different stream.
		if os.Getenv("VAULT_TOKEN") != "" {
			c.UI.Warn(wrapAtLength("WARNING! The VAULT_TOKEN environment variable "+
				"is set! This takes precedence over the value set by this command. To "+
				"use the value set by this command, unset the VAULT_TOKEN environment "+
				"variable or set it to the token displayed below.") + "\n")
		}
	} else if !c.flagTokenOnly {
		// If token-only the user knows it won't be stored, so don't warn
		c.UI.Warn(wrapAtLength(
			"The token was not stored in token helper. Set the VAULT_TOKEN "+
				"environment variable or pass the token below with each request to "+
				"Vault.") + "\n")
	}

	// If the user requested a particular field, print that out now since we
	// are likely piping to another process.
	if c.flagField != "" {
		return PrintRawField(c.UI, secret, c.flagField)
	}

	// Print some yay! text, but only in table mode.
	if Format(c.UI) == "table" {
		c.UI.Output(wrapAtLength(
			"Success! You are now authenticated. The token information displayed "+
				"below is already stored in the token helper. You do NOT need to run "+
				"\"vault login\" again. Future Vault requests will automatically use "+
				"this token.") + "\n")
	}

	return OutputSecret(c.UI, secret)
}

// extractToken extracts the token from the given secret, automatically
// unwrapping responses and handling error conditions if unwrap is true. The
// result also returns whether it was a wrapped response that was not unwrapped.
func (c *LoginCommand) extractToken(client *api.Client, secret *api.Secret, unwrap bool) (*api.Secret, bool, error) {
	switch {
	case secret == nil:
		return nil, false, fmt.Errorf("empty response from auth helper")

	case secret.Auth != nil:
		return secret, false, nil

	case secret.WrapInfo != nil:
		if secret.WrapInfo.WrappedAccessor == "" {
			return nil, false, fmt.Errorf("wrapped response does not contain a token")
		}

		if !unwrap {
			return secret, true, nil
		}

		client.SetToken(secret.WrapInfo.Token)
		secret, err := client.Logical().Unwrap("")
		if err != nil {
			return nil, false, err
		}
		return c.extractToken(client, secret, unwrap)

	default:
		return nil, false, fmt.Errorf("no auth or wrapping info in response")
	}
}
<commit_msg>Add -no-print to 'vault login' (#4454)<commit_after>package command

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/hashicorp/vault/api"
	"github.com/posener/complete"
)

// LoginHandler is the interface that any auth handlers must implement to enable
// auth via the CLI.
type LoginHandler interface {
	Auth(*api.Client, map[string]string) (*api.Secret, error)
	Help() string
}

type LoginCommand struct {
	*BaseCommand

	Handlers map[string]LoginHandler

	flagMethod    string
	flagPath      string
	flagNoStore   bool
	flagNoPrint   bool
	flagTokenOnly bool

	// Deprecations
	// TODO: remove in 0.9.0
	flagNoVerify bool

	testStdin io.Reader // for tests
}

func (c *LoginCommand) Synopsis() string {
	return "Authenticate locally"
}

func (c *LoginCommand) Help() string {
	helpText := `
Usage: vault login [options] [AUTH K=V...]

  Authenticates users or machines to Vault using the provided arguments. A
  successful authentication results in a Vault token - conceptually similar to
  a session token on a website. By default, this token is cached on the local
  machine for future requests.

  The default auth method is "token". If not supplied via the CLI,
  Vault will prompt for input. If the argument is "-", the values are read
  from stdin.

  The -method flag allows using other auth methods, such as userpass, github, or
  cert. For these, additional "K=V" pairs may be required. For example, to
  authenticate to the userpass auth method:

      $ vault login -method=userpass username=my-username

  For more information about the list of configuration parameters available for
  a given auth method, use the "vault auth help TYPE". You can also use "vault
  auth list" to see the list of enabled auth methods.

  If an auth method is enabled at a non-standard path, the -method flag still
  refers to the canonical type, but the -path flag refers to the enabled path.
  If a github auth method was enabled at "github-ent", authenticate like this:

      $ vault login -method=github -path=github-prod

  If the authentication is requested with response wrapping (via -wrap-ttl),
  the returned token is automatically unwrapped unless:

    - The -token-only flag is used, in which case this command will output
      the wrapping token.

    - The -no-store flag is used, in which case this command will output the
      details of the wrapping token.

` + c.Flags().Help()

	return strings.TrimSpace(helpText)
}

func (c *LoginCommand) Flags() *FlagSets {
	set := c.flagSet(FlagSetHTTP | FlagSetOutputField | FlagSetOutputFormat)

	f := set.NewFlagSet("Command Options")

	f.StringVar(&StringVar{
		Name:       "method",
		Target:     &c.flagMethod,
		Default:    "token",
		Completion: c.PredictVaultAvailableAuths(),
		Usage: "Type of authentication to use such as \"userpass\" or " +
			"\"ldap\". Note this corresponds to the TYPE, not the enabled path. " +
			"Use -path to specify the path where the authentication is enabled.",
	})

	f.StringVar(&StringVar{
		Name:       "path",
		Target:     &c.flagPath,
		Default:    "",
		Completion: c.PredictVaultAuths(),
		Usage: "Remote path in Vault where the auth method is enabled. " +
			"This defaults to the TYPE of method (e.g. userpass -> userpass/).",
	})

	f.BoolVar(&BoolVar{
		Name:    "no-store",
		Target:  &c.flagNoStore,
		Default: false,
		Usage: "Do not persist the token to the token helper (usually the " +
			"local filesystem) after authentication for use in future requests. " +
			"The token will only be displayed in the command output.",
	})

	f.BoolVar(&BoolVar{
		Name:    "no-print",
		Target:  &c.flagNoPrint,
		Default: false,
		Usage: "Do not display the token. The token will be still be stored to the " +
			"configured token helper.",
	})

	f.BoolVar(&BoolVar{
		Name:    "token-only",
		Target:  &c.flagTokenOnly,
		Default: false,
		Usage: "Output only the token with no verification. This flag is a " +
			"shortcut for \"-field=token -no-store\". Setting those flags to other " +
			"values will have no affect.",
	})

	// Deprecations
	// TODO: remove in 0.9.0
	f.BoolVar(&BoolVar{
		Name:    "no-verify",
		Target:  &c.flagNoVerify,
		Hidden:  true,
		Default: false,
		Usage:   "",
	})

	return set
}

func (c *LoginCommand) AutocompleteArgs() complete.Predictor {
	return nil
}

func (c *LoginCommand) AutocompleteFlags() complete.Flags {
	return c.Flags().Completions()
}

func (c *LoginCommand) Run(args []string) int {
	f := c.Flags()

	if err := f.Parse(args); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	args = f.Args()

	// Deprecations
	// TODO: remove in 0.10.0
	switch {
	case c.flagNoVerify:
		c.UI.Warn(wrapAtLength(
			"WARNING! The -no-verify flag is deprecated. In the past, Vault " +
				"performed a lookup on a token after authentication. This is no " +
				"longer the case for all auth methods except \"token\". Vault will " +
				"still attempt to perform a lookup when given a token directly " +
				"because that is how it gets the list of policies, ttl, and other " +
				"metadata. To disable this lookup, specify \"lookup=false\" as a " +
				"configuration option to the token auth method, like this:"))
		c.UI.Warn("")
		c.UI.Warn("    $ vault auth token=ABCD lookup=false")
		c.UI.Warn("")
		c.UI.Warn("Or omit the token and Vault will prompt for it:")
		c.UI.Warn("")
		c.UI.Warn("    $ vault auth lookup=false")
		c.UI.Warn("    Token (will be hidden): ...")
		c.UI.Warn("")
		c.UI.Warn(wrapAtLength(
			"If you are not using token authentication, you can safely omit this " +
				"flag. Vault will not perform a lookup after authentication."))
		c.UI.Warn("")

		// There's no point in passing this to other auth handlers...
		if c.flagMethod == "token" {
			args = append(args, "lookup=false")
		}
	}

	// Set the right flags if the user requested token-only - this overrides
	// any previously configured values, as documented.
	if c.flagTokenOnly {
		c.flagNoStore = true
		c.flagField = "token"
	}

	if c.flagNoStore && c.flagNoPrint {
		c.UI.Error(wrapAtLength(
			"-no-store and -no-print cannot be used together"))
		return 1
	}

	// Get the auth method
	authMethod := sanitizePath(c.flagMethod)
	if authMethod == "" {
		authMethod = "token"
	}

	// If no path is specified, we default the path to the method type
	// or use the plugin name if it's a plugin
	authPath := c.flagPath
	if authPath == "" {
		authPath = ensureTrailingSlash(authMethod)
	}

	// Get the handler function
	authHandler, ok := c.Handlers[authMethod]
	if !ok {
		c.UI.Error(wrapAtLength(fmt.Sprintf(
			"Unknown auth method: %s. Use \"vault auth list\" to see the "+
				"complete list of auth methods. Additionally, some "+
				"auth methods are only available via the HTTP API.",
			authMethod)))
		return 1
	}

	// Pull our fake stdin if needed
	stdin := (io.Reader)(os.Stdin)
	if c.testStdin != nil {
		stdin = c.testStdin
	}

	// If the user provided a token, pass it along to the auth provider.
	if authMethod == "token" && len(args) > 0 && !strings.Contains(args[0], "=") {
		args = append([]string{"token=" + args[0]}, args[1:]...)
	}

	config, err := parseArgsDataString(stdin, args)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error parsing configuration: %s", err))
		return 1
	}

	// If the user did not specify a mount path, use the provided mount path.
	if config["mount"] == "" && authPath != "" {
		config["mount"] = authPath
	}

	// Create the client
	client, err := c.Client()
	if err != nil {
		c.UI.Error(err.Error())
		return 2
	}

	// Authenticate delegation to the auth handler
	secret, err := authHandler.Auth(client, config)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error authenticating: %s", err))
		return 2
	}

	// Unset any previous token wrapping functionality. If the original request
	// was for a wrapped token, we don't want future requests to be wrapped.
	client.SetWrappingLookupFunc(func(string, string) string { return "" })

	// Recursively extract the token, handling wrapping
	unwrap := !c.flagTokenOnly && !c.flagNoStore
	secret, isWrapped, err := c.extractToken(client, secret, unwrap)
	if err != nil {
		c.UI.Error(fmt.Sprintf("Error extracting token: %s", err))
		return 2
	}
	if secret == nil {
		c.UI.Error("Vault returned an empty secret")
		return 2
	}

	// Handle special cases if the token was wrapped
	if isWrapped {
		if c.flagTokenOnly {
			return PrintRawField(c.UI, secret, "wrapping_token")
		}
		if c.flagNoStore {
			return OutputSecret(c.UI, secret)
		}
	}

	// If we got this far, verify we have authentication data before continuing
	if secret.Auth == nil {
		c.UI.Error(wrapAtLength(
			"Vault returned a secret, but the secret has no authentication " +
				"information attached. This should never happen and is likely a " +
				"bug."))
		return 2
	}

	// Pull the token itself out, since we don't need the rest of the auth
	// information anymore/.
	token := secret.Auth.ClientToken

	if !c.flagNoStore {
		// Grab the token helper so we can store
		tokenHelper, err := c.TokenHelper()
		if err != nil {
			c.UI.Error(wrapAtLength(fmt.Sprintf(
				"Error initializing token helper. Please verify that the token "+
					"helper is available and properly configured for your system. The "+
					"error was: %s", err)))
			return 1
		}

		// Store the token in the local client
		if err := tokenHelper.Store(token); err != nil {
			c.UI.Error(fmt.Sprintf("Error storing token: %s", err))
			c.UI.Error(wrapAtLength(
				"Authentication was successful, but the token was not persisted. The "+
					"resulting token is shown below for your records.") + "\n")
			OutputSecret(c.UI, secret)
			return 2
		}

		// Warn if the VAULT_TOKEN environment variable is set, as that will take
		// precedence. We output as a warning, so piping should still work since it
		// will be on a different stream.
		if os.Getenv("VAULT_TOKEN") != "" {
			c.UI.Warn(wrapAtLength("WARNING! The VAULT_TOKEN environment variable "+
				"is set! This takes precedence over the value set by this command. To "+
				"use the value set by this command, unset the VAULT_TOKEN environment "+
				"variable or set it to the token displayed below.") + "\n")
		}
	} else if !c.flagTokenOnly {
		// If token-only the user knows it won't be stored, so don't warn
		c.UI.Warn(wrapAtLength(
			"The token was not stored in token helper. Set the VAULT_TOKEN "+
				"environment variable or pass the token below with each request to "+
				"Vault.") + "\n")
	}

	if c.flagNoPrint {
		return 0
	}

	// If the user requested a particular field, print that out now since we
	// are likely piping to another process.
	if c.flagField != "" {
		return PrintRawField(c.UI, secret, c.flagField)
	}

	// Print some yay! text, but only in table mode.
	if Format(c.UI) == "table" {
		c.UI.Output(wrapAtLength(
			"Success! You are now authenticated. The token information displayed "+
				"below is already stored in the token helper. You do NOT need to run "+
				"\"vault login\" again. Future Vault requests will automatically use "+
				"this token.") + "\n")
	}

	return OutputSecret(c.UI, secret)
}

// extractToken extracts the token from the given secret, automatically
// unwrapping responses and handling error conditions if unwrap is true. The
// result also returns whether it was a wrapped response that was not unwrapped.
func (c *LoginCommand) extractToken(client *api.Client, secret *api.Secret, unwrap bool) (*api.Secret, bool, error) {
	switch {
	case secret == nil:
		return nil, false, fmt.Errorf("empty response from auth helper")

	case secret.Auth != nil:
		return secret, false, nil

	case secret.WrapInfo != nil:
		if secret.WrapInfo.WrappedAccessor == "" {
			return nil, false, fmt.Errorf("wrapped response does not contain a token")
		}

		if !unwrap {
			return secret, true, nil
		}

		client.SetToken(secret.WrapInfo.Token)
		secret, err := client.Logical().Unwrap("")
		if err != nil {
			return nil, false, err
		}
		return c.extractToken(client, secret, unwrap)

	default:
		return nil, false, fmt.Errorf("no auth or wrapping info in response")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"testing"

	"github.com/urfave/cli"
)

func flagSet(name string, flags []cli.Flag) *flag.FlagSet {
	set := flag.NewFlagSet(name, flag.ContinueOnError)

	for _, f := range flags {
		f.Apply(set)
	}
	return set
}

func captureReader(block func()) (*os.File, *os.File, error) {
	rOut, wOut, err := os.Pipe()
	if err != nil {
		return nil, nil, err
	}

	rErr, wErr, err := os.Pipe()
	if err != nil {
		return nil, nil, err
	}

	defer wOut.Close()
	defer wErr.Close()

	var stdout, stderr *os.File
	os.Stdout, stdout = wOut, os.Stdout
	os.Stderr, stderr = wErr, os.Stderr

	defer func() {
		os.Stdout = stdout
		os.Stderr = stderr
	}()

	block()

	wOut.Close()
	wErr.Close()

	return rOut, rErr, nil
}

func capture(block func()) (string, string, error) {
	rOut, rErr, err := captureReader(block)
	if err != nil {
		return "", "", err
	}

	bufOut, err := ioutil.ReadAll(rOut)
	if err != nil {
		return "", "", err
	}

	bufErr, err := ioutil.ReadAll(rErr)
	if err != nil {
		return "", "", err
	}

	return string(bufOut), string(bufErr), nil
}

type _cloneArgs struct {
	remote  *url.URL
	local   string
	shallow bool
}

type _updateArgs struct {
	local string
}

func withFakeGitBackend(t *testing.T, block func(*testing.T, string, *_cloneArgs, *_updateArgs)) {
	tmpRoot, err := ioutil.TempDir("", "ghq-test")
	if err != nil {
		t.Fatalf("Could not create tempdir: %s", err)
	}
	defer os.RemoveAll(tmpRoot)

	// Resolve /var/folders/.../T/... to /private/var/... in OSX
	tmpRoot = func() string {
		wd, err := os.Getwd()
		if err != nil {
			t.Fatalf("os.Getwd(): %s", err)
		}

		defer func() {
			err := os.Chdir(wd)
			if err != nil {
				t.Fatalf("os.Chdir(): %s", err)
			}
		}()

		err = os.Chdir(tmpRoot)
		if err != nil {
			t.Fatalf("os.Chdir(): %s", err)
		}

		tmpRoot, err := os.Getwd()
		if err != nil {
			t.Fatalf("os.Getwd(): %s", err)
		}

		return tmpRoot
	}()

	defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots)
	_localRepositoryRoots = []string{tmpRoot}

	var cloneArgs _cloneArgs
	var updateArgs _updateArgs

	var originalGitBackend = GitBackend
	tmpBackend := &VCSBackend{
		Clone: func(remote *url.URL, local string, shallow, silent bool) error {
			cloneArgs = _cloneArgs{
				remote:  remote,
				local:   filepath.FromSlash(local),
				shallow: shallow,
			}
			return nil
		},
		Update: func(local string, silent bool) error {
			updateArgs = _updateArgs{
				local: local,
			}
			return nil
		},
	}
	GitBackend = tmpBackend
	vcsDirsMap[".git"] = tmpBackend
	defer func() { GitBackend = originalGitBackend; vcsDirsMap[".git"] = originalGitBackend }()

	block(t, tmpRoot, &cloneArgs, &updateArgs)
}

func TestCommandGet(t *testing.T) {
	app := newApp()

	testCases := []struct {
		name     string
		scenario func(*testing.T, string, *_cloneArgs, *_updateArgs)
	}{
		{
			name: "simple",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")

				app.Run([]string{"", "get", "motemen/ghq-test-repo"})

				expect := "https://github.com/motemen/ghq-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
					t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
				}
				if cloneArgs.shallow {
					t.Errorf("cloneArgs.shallow should be false")
				}
			},
		},
		{
			name: "-p option",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")

				app.Run([]string{"", "get", "-p", "motemen/ghq-test-repo"})

				expect := "ssh://git@github.com/motemen/ghq-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
					t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
				}
				if cloneArgs.shallow {
					t.Errorf("cloneArgs.shallow should be false")
				}
			},
		},
		{
			name: "already cloned with -u",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")
				// mark as "already cloned", the condition may change later
				os.MkdirAll(filepath.Join(localDir, ".git"), 0755)

				app.Run([]string{"", "get", "-u", "motemen/ghq-test-repo"})

				if updateArgs.local != localDir {
					t.Errorf("got: %s, expect: %s", updateArgs.local, localDir)
				}
			},
		},
		{
			name: "shallow",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")

				app.Run([]string{"", "get", "-shallow", "motemen/ghq-test-repo"})

				expect := "https://github.com/motemen/ghq-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
					t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
				}
				if !cloneArgs.shallow {
					t.Errorf("cloneArgs.shallow should be true")
				}
			},
		},
		{
			name: "dot slach ./",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen")
				os.MkdirAll(localDir, 0755)
				wd, _ := os.Getwd()
				os.Chdir(localDir)
				defer os.Chdir(wd)

				app.Run([]string{"", "get", "-u", "." + string(filepath.Separator) + "ghq-test-repo"})

				expect := "https://github.com/motemen/ghq-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				expectDir := filepath.Join(localDir, "ghq-test-repo")
				if cloneArgs.local != expectDir {
					t.Errorf("got: %s, expect: %s", cloneArgs.local, expectDir)
				}
			},
		},
		{
			name: "dot dot slash ../",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")
				os.MkdirAll(localDir, 0755)
				wd, _ := os.Getwd()
				os.Chdir(localDir)
				defer os.Chdir(wd)

				app.Run([]string{"", "get", "-u", ".." + string(filepath.Separator) + "ghq-another-test-repo"})

				expect := "https://github.com/motemen/ghq-another-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				expectDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-another-test-repo")
				if cloneArgs.local != expectDir {
					t.Errorf("got: %s, expect: %s", cloneArgs.local, expectDir)
				}
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			withFakeGitBackend(t, tc.scenario)
		})
	}
}

func TestCommandList(t *testing.T) {
	_, _, err := capture(func() {
		app := cli.NewApp()
		flagSet := flagSet("list", commandList.Flags)
		c := cli.NewContext(app, flagSet, nil)

		doList(c)
	})

	if err != nil {
		t.Errorf("error should be nil, but: %s", err)
	}
}

func TestCommandListUnique(t *testing.T) {
	_, _, err := capture(func() {
		app := cli.NewApp()
		flagSet := flagSet("list", commandList.Flags)
		flagSet.Parse([]string{"--unique"})
		c := cli.NewContext(app, flagSet, nil)

		doList(c)
	})

	if err != nil {
		t.Errorf("error should be nil, but: %s", err)
	}
}

func TestCommandListUnknown(t *testing.T) {
	_, _, err := capture(func() {
		app := cli.NewApp()
		flagSet := flagSet("list", commandList.Flags)
		flagSet.Parse([]string{"--unknown-flag"})
		c := cli.NewContext(app, flagSet, nil)

		doList(c)
	})

	if err != nil {
		t.Errorf("error should be nil, but: %s", err)
	}
}
<commit_msg>add TestDoRoot<commit_after>package main

import (
	"flag"
	"io/ioutil"
	"net/url"
	"os"
	"path/filepath"
	"testing"

	"github.com/urfave/cli"
)

func flagSet(name string, flags []cli.Flag) *flag.FlagSet {
	set := flag.NewFlagSet(name, flag.ContinueOnError)

	for _, f := range flags {
		f.Apply(set)
	}
	return set
}

func captureReader(block func()) (*os.File, *os.File, error) {
	rOut, wOut, err := os.Pipe()
	if err != nil {
		return nil, nil, err
	}

	rErr, wErr, err := os.Pipe()
	if err != nil {
		return nil, nil, err
	}

	defer wOut.Close()
	defer wErr.Close()

	var stdout, stderr *os.File
	os.Stdout, stdout = wOut, os.Stdout
	os.Stderr, stderr = wErr, os.Stderr

	defer func() {
		os.Stdout = stdout
		os.Stderr = stderr
	}()

	block()

	wOut.Close()
	wErr.Close()

	return rOut, rErr, nil
}

func capture(block func()) (string, string, error) {
	rOut, rErr, err := captureReader(block)
	if err != nil {
		return "", "", err
	}
	defer rOut.Close()
	defer rErr.Close()

	bufOut, err := ioutil.ReadAll(rOut)
	if err != nil {
		return "", "", err
	}

	bufErr, err := ioutil.ReadAll(rErr)
	if err != nil {
		return "", "", err
	}

	return string(bufOut), string(bufErr), nil
}

type _cloneArgs struct {
	remote  *url.URL
	local   string
	shallow bool
}

type _updateArgs struct {
	local string
}

func withFakeGitBackend(t *testing.T, block func(*testing.T, string, *_cloneArgs, *_updateArgs)) {
	tmpRoot, err := ioutil.TempDir("", "ghq-test")
	if err != nil {
		t.Fatalf("Could not create tempdir: %s", err)
	}
	defer os.RemoveAll(tmpRoot)

	// Resolve /var/folders/.../T/... to /private/var/... in OSX
	tmpRoot = func() string {
		wd, err := os.Getwd()
		if err != nil {
			t.Fatalf("os.Getwd(): %s", err)
		}

		defer func() {
			err := os.Chdir(wd)
			if err != nil {
				t.Fatalf("os.Chdir(): %s", err)
			}
		}()

		err = os.Chdir(tmpRoot)
		if err != nil {
			t.Fatalf("os.Chdir(): %s", err)
		}

		tmpRoot, err := os.Getwd()
		if err != nil {
			t.Fatalf("os.Getwd(): %s", err)
		}

		return tmpRoot
	}()

	defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots)
	_localRepositoryRoots = []string{tmpRoot}

	var cloneArgs _cloneArgs
	var updateArgs _updateArgs

	var originalGitBackend = GitBackend
	tmpBackend := &VCSBackend{
		Clone: func(remote *url.URL, local string, shallow, silent bool) error {
			cloneArgs = _cloneArgs{
				remote:  remote,
				local:   filepath.FromSlash(local),
				shallow: shallow,
			}
			return nil
		},
		Update: func(local string, silent bool) error {
			updateArgs = _updateArgs{
				local: local,
			}
			return nil
		},
	}
	GitBackend = tmpBackend
	vcsDirsMap[".git"] = tmpBackend
	defer func() { GitBackend = originalGitBackend; vcsDirsMap[".git"] = originalGitBackend }()

	block(t, tmpRoot, &cloneArgs, &updateArgs)
}

func TestCommandGet(t *testing.T) {
	app := newApp()

	testCases := []struct {
		name     string
		scenario func(*testing.T, string, *_cloneArgs, *_updateArgs)
	}{
		{
			name: "simple",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")

				app.Run([]string{"", "get", "motemen/ghq-test-repo"})

				expect := "https://github.com/motemen/ghq-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
					t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
				}
				if cloneArgs.shallow {
					t.Errorf("cloneArgs.shallow should be false")
				}
			},
		},
		{
			name: "-p option",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")

				app.Run([]string{"", "get", "-p", "motemen/ghq-test-repo"})

				expect := "ssh://git@github.com/motemen/ghq-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
					t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
				}
				if cloneArgs.shallow {
					t.Errorf("cloneArgs.shallow should be false")
				}
			},
		},
		{
			name: "already cloned with -u",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")
				// mark as "already cloned", the condition may change later
				os.MkdirAll(filepath.Join(localDir, ".git"), 0755)

				app.Run([]string{"", "get", "-u", "motemen/ghq-test-repo"})

				if updateArgs.local != localDir {
					t.Errorf("got: %s, expect: %s", updateArgs.local, localDir)
				}
			},
		},
		{
			name: "shallow",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")

				app.Run([]string{"", "get", "-shallow", "motemen/ghq-test-repo"})

				expect := "https://github.com/motemen/ghq-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
					t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
				}
				if !cloneArgs.shallow {
					t.Errorf("cloneArgs.shallow should be true")
				}
			},
		},
		{
			name: "dot slach ./",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen")
				os.MkdirAll(localDir, 0755)
				wd, _ := os.Getwd()
				os.Chdir(localDir)
				defer os.Chdir(wd)

				app.Run([]string{"", "get", "-u", "." + string(filepath.Separator) + "ghq-test-repo"})

				expect := "https://github.com/motemen/ghq-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				expectDir := filepath.Join(localDir, "ghq-test-repo")
				if cloneArgs.local != expectDir {
					t.Errorf("got: %s, expect: %s", cloneArgs.local, expectDir)
				}
			},
		},
		{
			name: "dot dot slash ../",
			scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
				localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")
				os.MkdirAll(localDir, 0755)
				wd, _ := os.Getwd()
				os.Chdir(localDir)
				defer os.Chdir(wd)

				app.Run([]string{"", "get", "-u", ".." + string(filepath.Separator) + "ghq-another-test-repo"})

				expect := "https://github.com/motemen/ghq-another-test-repo"
				if cloneArgs.remote.String() != expect {
					t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
				}
				expectDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-another-test-repo")
				if cloneArgs.local != expectDir {
					t.Errorf("got: %s, expect: %s", cloneArgs.local, expectDir)
				}
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			withFakeGitBackend(t, tc.scenario)
		})
	}
}

func TestCommandList(t *testing.T) {
	_, _, err := capture(func() {
		app := cli.NewApp()
		flagSet := flagSet("list", commandList.Flags)
		c := cli.NewContext(app, flagSet, nil)

		doList(c)
	})

	if err != nil {
		t.Errorf("error should be nil, but: %s", err)
	}
}

func TestCommandListUnique(t *testing.T) {
	_, _, err := capture(func() {
		app := cli.NewApp()
		flagSet := flagSet("list", commandList.Flags)
		flagSet.Parse([]string{"--unique"})
		c := cli.NewContext(app, flagSet, nil)

		doList(c)
	})

	if err != nil {
		t.Errorf("error should be nil, but: %s", err)
	}
}

func TestCommandListUnknown(t *testing.T) {
	_, _, err := capture(func() {
		app := cli.NewApp()
		flagSet := flagSet("list", commandList.Flags)
		flagSet.Parse([]string{"--unknown-flag"})
		c := cli.NewContext(app, flagSet, nil)

		doList(c)
	})

	if err != nil {
		t.Errorf("error should be nil, but: %s", err)
	}
}

func TestDoRoot(t *testing.T) {
	ghqrootEnv := "GHQ_ROOT"
	testCases := []struct {
		name              string
		setup             func() func()
		expect, allExpect string
	}{{
		name: "env",
		setup: func() func() {
			orig := os.Getenv(ghqrootEnv)
			os.Setenv(ghqrootEnv, "/path/to/ghqroot1"+string(os.PathListSeparator)+"/path/to/ghqroot2")
			return func() { os.Setenv(ghqrootEnv, orig) }
		},
		expect:    "/path/to/ghqroot1\n",
		allExpect: "/path/to/ghqroot1\n/path/to/ghqroot2\n",
	}, {
		name: "gitconfig",
		setup: func() func() {
			orig := os.Getenv(ghqrootEnv)
			os.Setenv(ghqrootEnv, "")
			teardown, err := WithGitconfigFile(`[ghq]
  root = /path/to/ghqroot11
  root = /path/to/ghqroot12
`)
			if err != nil {
				panic(err)
			}
			return func() {
				os.Setenv(ghqrootEnv, orig)
				teardown()
			}
		},
		expect:    "/path/to/ghqroot11\n",
		allExpect: "/path/to/ghqroot11\n/path/to/ghqroot12\n",
	}, {
		name: "default home",
		setup: func() func() {
			origRoot := os.Getenv(ghqrootEnv)
			os.Setenv(ghqrootEnv, "")
			origGitconfig := os.Getenv("GIT_CONFIG")
			os.Setenv("GIT_CONFIG", "/tmp/unknown-ghq-dummy")
			origHome := os.Getenv("HOME")
			os.Setenv("HOME", "/path/to/ghqhome")

			return func() {
				os.Setenv(ghqrootEnv, origRoot)
				os.Setenv("GIT_CONFIG", origGitconfig)
				os.Setenv("HOME", origHome)
			}
		},
		expect:    "/path/to/ghqhome/.ghq\n",
		allExpect: "/path/to/ghqhome/.ghq\n",
	}}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			defer func(orig []string) { _localRepositoryRoots = orig }(_localRepositoryRoots)
			_localRepositoryRoots = nil
			defer tc.setup()()
			out, _, _ := capture(func() {
				newApp().Run([]string{"", "root"})
			})
			if out != tc.expect {
				t.Errorf("got: %s, expect: %s", out, tc.expect)
			}
			out, _, _ = capture(func() {
				newApp().Run([]string{"", "root", "--all"})
			})
			if out != tc.allExpect {
				t.Errorf("got: %s, expect: %s", out, tc.allExpect)
			}
		})
	}
}
<|endoftext|>


<commit_before>package dashboard

import (
	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
	"github.com/animenotifier/notify.moe/pages/frontpage"
	"github.com/animenotifier/notify.moe/utils"
)

const maxPosts = 5
const maxFollowing = 5

// Get dashboard.
func Get(ctx *aero.Context) string {
	user := utils.GetUser(ctx)

	if user == nil {
		return frontpage.Get(ctx)
	}

	posts, err := arn.GetPosts()

	if err != nil {
		return ctx.Error(500, "Error fetching posts", err)
	}

	arn.SortPostsLatestFirst(posts)

	if len(posts) > maxPosts {
		posts = posts[:maxPosts]
	}

	followIDList := user.Following
	var followingList []*arn.User

	if len(followIDList) > maxFollowing {
		followIDList = followIDList[:maxFollowing]
	}

	userList, err := arn.DB.GetMany("User", followIDList)

	if err != nil {
		return ctx.Error(500, "Error fetching followed users", err)
	}

	followingList = userList.([]*arn.User)

	return ctx.HTML(components.Dashboard(posts, followingList))
}
<commit_msg>Updated dashboard to use latest ARN API<commit_after>package dashboard

import (
	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/components"
	"github.com/animenotifier/notify.moe/pages/frontpage"
	"github.com/animenotifier/notify.moe/utils"
)

const maxPosts = 5
const maxFollowing = 5

// Get dashboard.
func Get(ctx *aero.Context) string {
	user := utils.GetUser(ctx)

	if user == nil {
		return frontpage.Get(ctx)
	}

	posts, err := arn.AllPostsSlice()

	if err != nil {
		return ctx.Error(500, "Error fetching posts", err)
	}

	arn.SortPostsLatestFirst(posts)

	if len(posts) > maxPosts {
		posts = posts[:maxPosts]
	}

	followIDList := user.Following
	var followingList []*arn.User

	if len(followIDList) > maxFollowing {
		followIDList = followIDList[:maxFollowing]
	}

	userList, err := arn.DB.GetMany("User", followIDList)

	if err != nil {
		return ctx.Error(500, "Error fetching followed users", err)
	}

	followingList = userList.([]*arn.User)

	return ctx.HTML(components.Dashboard(posts, followingList))
}
<|endoftext|>


<commit_before>package modbusone

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"sync/atomic"
	"time"
)

//FailoverRTUClient implements Client/Master side logic for RTU over a SerialContext to
//be used by a ProtocolHandler with failover function.
type FailoverRTUClient struct {
	com                  *FailoverSerialConn
	packetReader         PacketReader
	SlaveID              byte
	serverProcessingTime time.Duration
	actions              chan rtuAction
}

//FailoverRTUClient is also a Server
var _ Server = &FailoverRTUClient{}

//NewFailoverRTUClient create a new client with failover function communicating over SerialContext with the
//give slaveID as default.
//
//If isFailover is true, it is the secondary.
func NewFailoverRTUClient(com SerialContext, isFailover bool, slaveID byte) *FailoverRTUClient {
	pr, ok := com.(*FailoverSerialConn)
	if !ok {
		pr = NewFailoverConn(com, isFailover, true)
	}
	if pr.isFailover != isFailover {
		panic("A SerialContext was provided with conflicting settings.")
	}
	r := FailoverRTUClient{
		com:                  pr,
		packetReader:         pr,
		SlaveID:              slaveID,
		serverProcessingTime: time.Second,
		actions:              make(chan rtuAction),
	}
	return &r
}

//SetServerProcessingTime sets the time to wait for a server response, the total
//wait time also includes the time needed for data transmission
func (c *FailoverRTUClient) SetServerProcessingTime(t time.Duration) {
	c.serverProcessingTime = t
}

//GetTransactionTimeOut returns the total time to wait for a transaction
//(server response) to time out, given the expected length of RTU packets.
//This function is also used internally to calculate timeout.
func (c *FailoverRTUClient) GetTransactionTimeOut(reqLen, ansLen int) time.Duration {
	l := reqLen + ansLen
	return c.com.BytesDelay(l) + c.serverProcessingTime
}

//Serve serves FailoverRTUClient side handlers,
//
//A FailoverRTUClient expects a lot of "unexpected" read packets and "lost" writes so it
//is does not do the error checking that a normal client does, but instead try to guess the best
//interpretation.
func (c *FailoverRTUClient) Serve(handler ProtocolHandler) error {
	debugf("serve routine for %v", c.com.describe())
	defer c.Close()
	go func() {
		debugf("reader routine for %v", c.com.describe())
		//Reader loop that always ready to received data. This make sure that read
		//data is always new(ish), to dump data out that is received during an
		//unexpected time.
		for {
			rb := make([]byte, MaxRTUSize)
			n, err := c.packetReader.Read(rb)
			if err != nil {
				debugf("RTUClient read err:%v\n", err)
				c.actions <- rtuAction{t: clientError, err: err}
				c.Close()
				break
			}
			r := RTU(rb[:n])
			debugf("RTUClient read packet:%v\n", hex.EncodeToString(r))
			c.actions <- rtuAction{t: clientRead, data: r}
		}
	}()

	var last bytes.Buffer
	readUnexpected := func(act rtuAction, otherwise func()) {
		if act.err != nil || act.t != clientRead || len(act.data) == 0 {
			debugf("do not hand unexpected: %v", act)
			otherwise()
			return
		}
		debugf("handling unexpected: %v", act)
		pdu, err := act.data.GetPDU()
		if err != nil {
			debugf("readUnexpected GetPDU error: %v", err)
			otherwise()
			return
		}
		if !IsRequestReply(last.Bytes(), pdu) {
			if last.Len() != 0 {
				atomic.AddInt64(&c.com.Stats().OtherDrops, 1)
			}
			last.Reset()
			last.Write(pdu)
			return
		}
		defer last.Reset()

		if pdu.GetFunctionCode().IsWriteToServer() {
			//no-op for us
			return
		}

		bs, err := pdu.GetReplyValues()
		if err != nil {
			debugf("readUnexpected GetReplyValues error: %v", err)
			otherwise()
			return
		}
		err = handler.OnWrite(last.Bytes(), bs)
		if err != nil {
			debugf("readUnexpected OnWrite error: %v", err)
			otherwise()
			return
		}
	}

	for {
		act := <-c.actions
		switch act.t {
		default:
			readUnexpected(act, func() {
				atomic.AddInt64(&c.com.Stats().OtherDrops, 1)
				debugf("RTUClient drop unexpected: %v", act)
			})
			continue
		case clientError:
			return act.err
		case clientStart:
		}
		ap := act.data.fastGetPDU()
		afc := ap.GetFunctionCode()
		if afc.IsWriteToServer() {
			data, err := handler.OnRead(ap)
			if err != nil {
				act.errChan <- err
				continue
			}
			act.data = MakeRTU(act.data[0], ap.MakeWriteRequest(data))
			ap = act.data.fastGetPDU()
		}
		time.Sleep(c.com.MinDelay())
		_, err := c.com.Write(act.data)
		if err != nil {
			act.errChan <- err
			return err
		}
		c.com.lock.Lock()
		active := c.com.isActive
		c.com.lock.Unlock()
		if act.data[0] == 0 || !active {
			time.Sleep(c.com.BytesDelay(len(act.data)))
			act.errChan <- nil //always success
			continue           // do not wait for read on multicast or when not active
		}

		timeOutChan := time.After(c.GetTransactionTimeOut(len(act.data), MaxRTUSize))

	READ_LOOP:
		for {
		SELECT:
			select {
			case <-timeOutChan:
				act.errChan <- ErrServerTimeOut
				break READ_LOOP
			case react := <-c.actions:
				switch react.t {
				default:
					err := fmt.Errorf("unexpected action:%s", react.t)
					act.errChan <- err
					return err
				case clientError:
					return react.err
				case clientRead:
					//test for read error
					if react.err != nil {
						return react.err
					}
				}
				if react.data[0] != act.data[0] {
					atomic.AddInt64(&c.com.Stats().IDDrops, 1)
					debugf("RTUClient unexpected slaveId:%v in %v\n", act.data[0], hex.EncodeToString(react.data))
					break SELECT
				}
				rp, err := react.data.GetPDU()
				if err != nil {
					if err == ErrorCrc {
						atomic.AddInt64(&c.com.Stats().CrcErrors, 1)
					} else {
						atomic.AddInt64(&c.com.Stats().OtherErrors, 1)
					}
					act.errChan <- err
					break READ_LOOP
				}
				hasErr, fc := rp.GetFunctionCode().SeparateError()
				if hasErr && fc == afc {
					atomic.AddInt64(&c.com.Stats().RemoteErrors, 1)
					handler.OnError(ap, rp)
					act.errChan <- fmt.Errorf("server reply with exception:%v", hex.EncodeToString(rp))
					break READ_LOOP
				}
				if !IsRequestReply(act.data.fastGetPDU(), rp) {
					readUnexpected(act, func() {
						atomic.AddInt64(&c.com.Stats().OtherErrors, 1)
					})
					act.errChan <- fmt.Errorf("unexpected reply:%v", hex.EncodeToString(rp))
					break READ_LOOP
				}
				if afc.IsReadToServer() {
					//read from server, write here
					bs, err := rp.GetReplyValues()
					if err != nil {
						atomic.AddInt64(&c.com.Stats().OtherErrors, 1)
						act.errChan <- err
						break READ_LOOP
					}
					err = handler.OnWrite(ap, bs)
					if err != nil {
						atomic.AddInt64(&c.com.Stats().OtherErrors, 1)
					}
					act.errChan <- err //success if nil
					break READ_LOOP
				}
				act.errChan <- nil //success
				break READ_LOOP
			}
		}
	}
}

//Close closes the client and closes the connect
func (c *FailoverRTUClient) Close() error {
	return c.com.Close()
}

//DoTransaction starts a transaction, and returns a channel that returns an error
//or nil, with the default slaveID.
//
//DoTransaction is blocking.
//
//For read from server, the PDU is sent as is (after been warped up in RTU)
//For write to server, the data part given will be ignored, and filled in by data from handler.
func (c *FailoverRTUClient) DoTransaction(req PDU) error {
	errChan := make(chan error)
	c.StartTransactionToServer(c.SlaveID, req, errChan)
	return <-errChan
}

//StartTransactionToServer starts a transaction, with a custom slaveID.
//errChan is required and usable, an error is set is the transaction failed, or
//nil for success.
//
//StartTransactionToServer is not blocking.
//
//For read from server, the PDU is sent as is (after been warped up in RTU)
//For write to server, the data part given will be ignored, and filled in by data from handler.
func (c *FailoverRTUClient) StartTransactionToServer(slaveID byte, req PDU, errChan chan error) {
	c.actions <- rtuAction{t: clientStart, data: MakeRTU(slaveID, req), errChan: errChan}
}
<commit_msg>better debug messages<commit_after>package modbusone

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"sync/atomic"
	"time"
)

//FailoverRTUClient implements Client/Master side logic for RTU over a SerialContext to
//be used by a ProtocolHandler with failover function.
type FailoverRTUClient struct {
	com                  *FailoverSerialConn
	packetReader         PacketReader
	SlaveID              byte
	serverProcessingTime time.Duration
	actions              chan rtuAction
}

//FailoverRTUClient is also a Server
var _ Server = &FailoverRTUClient{}

//NewFailoverRTUClient create a new client with failover function communicating over SerialContext with the
//give slaveID as default.
//
//If isFailover is true, it is the secondary.
func NewFailoverRTUClient(com SerialContext, isFailover bool, slaveID byte) *FailoverRTUClient {
	pr, ok := com.(*FailoverSerialConn)
	if !ok {
		pr = NewFailoverConn(com, isFailover, true)
	}
	if pr.isFailover != isFailover {
		panic("A SerialContext was provided with conflicting settings.")
	}
	r := FailoverRTUClient{
		com:                  pr,
		packetReader:         pr,
		SlaveID:              slaveID,
		serverProcessingTime: time.Second,
		actions:              make(chan rtuAction),
	}
	return &r
}

//SetServerProcessingTime sets the time to wait for a server response, the total
//wait time also includes the time needed for data transmission
func (c *FailoverRTUClient) SetServerProcessingTime(t time.Duration) {
	c.serverProcessingTime = t
}

//GetTransactionTimeOut returns the total time to wait for a transaction
//(server response) to time out, given the expected length of RTU packets.
//This function is also used internally to calculate timeout.
func (c *FailoverRTUClient) GetTransactionTimeOut(reqLen, ansLen int) time.Duration {
	l := reqLen + ansLen
	return c.com.BytesDelay(l) + c.serverProcessingTime
}

//Serve serves FailoverRTUClient side handlers,
//
//A FailoverRTUClient expects a lot of "unexpected" read packets and "lost" writes so it
//is does not do the error checking that a normal client does, but instead try to guess the best
//interpretation.
func (c *FailoverRTUClient) Serve(handler ProtocolHandler) error {
	debugf("serve routine for %v", c.com.describe())
	defer c.Close()
	go func() {
		debugf("reader routine for %v", c.com.describe())
		//Reader loop that always ready to received data. This make sure that read
		//data is always new(ish), to dump data out that is received during an
		//unexpected time.
		for {
			rb := make([]byte, MaxRTUSize)
			n, err := c.packetReader.Read(rb)
			if err != nil {
				debugf("FailoverRTUClient read err:%v\n", err)
				c.actions <- rtuAction{t: clientError, err: err}
				c.Close()
				break
			}
			r := RTU(rb[:n])
			debugf("FailoverRTUClient read packet:%v\n", hex.EncodeToString(r))
			c.actions <- rtuAction{t: clientRead, data: r}
		}
	}()

	var last bytes.Buffer
	readUnexpected := func(act rtuAction, otherwise func()) {
		if act.err != nil || act.t != clientRead || len(act.data) == 0 {
			debugf("do not hand unexpected: %v", act)
			otherwise()
			return
		}
		debugf("handling unexpected: %v", act)
		pdu, err := act.data.GetPDU()
		if err != nil {
			debugf("readUnexpected GetPDU error: %v", err)
			otherwise()
			return
		}
		if !IsRequestReply(last.Bytes(), pdu) {
			if last.Len() != 0 {
				atomic.AddInt64(&c.com.Stats().OtherDrops, 1)
			}
			last.Reset()
			last.Write(pdu)
			return
		}
		defer last.Reset()

		if pdu.GetFunctionCode().IsWriteToServer() {
			//no-op for us
			return
		}

		bs, err := pdu.GetReplyValues()
		if err != nil {
			debugf("readUnexpected GetReplyValues error: %v", err)
			otherwise()
			return
		}
		err = handler.OnWrite(last.Bytes(), bs)
		if err != nil {
			debugf("readUnexpected OnWrite error: %v", err)
			otherwise()
			return
		}
	}

	for {
		act := <-c.actions
		switch act.t {
		default:
			readUnexpected(act, func() {
				atomic.AddInt64(&c.com.Stats().OtherDrops, 1)
				debugf("FailoverRTUClient drop unexpected: %v", act)
			})
			continue
		case clientError:
			return act.err
		case clientStart:
		}
		ap := act.data.fastGetPDU()
		afc := ap.GetFunctionCode()
		if afc.IsWriteToServer() {
			data, err := handler.OnRead(ap)
			if err != nil {
				act.errChan <- err
				continue
			}
			act.data = MakeRTU(act.data[0], ap.MakeWriteRequest(data))
			ap = act.data.fastGetPDU()
		}
		time.Sleep(c.com.MinDelay())
		_, err := c.com.Write(act.data)
		if err != nil {
			act.errChan <- err
			return err
		}
		c.com.lock.Lock()
		active := c.com.isActive
		c.com.lock.Unlock()
		if act.data[0] == 0 || !active {
			debugf("FailoverRTUClient skip action:%v\n", act)
			time.Sleep(c.com.BytesDelay(len(act.data)))
			act.errChan <- nil //always success
			continue           // do not wait for read on multicast or when not active
		}

		timeOutChan := time.After(c.GetTransactionTimeOut(len(act.data), MaxRTUSize))

	READ_LOOP:
		for {
		SELECT:
			select {
			case <-timeOutChan:
				act.errChan <- ErrServerTimeOut
				break READ_LOOP
			case react := <-c.actions:
				switch react.t {
				default:
					err := fmt.Errorf("unexpected action:%s", react.t)
					act.errChan <- err
					return err
				case clientError:
					return react.err
				case clientRead:
					//test for read error
					if react.err != nil {
						return react.err
					}
				}
				if react.data[0] != act.data[0] {
					atomic.AddInt64(&c.com.Stats().IDDrops, 1)
					debugf("FailoverRTUClient unexpected slaveId:%v in %v\n", act.data[0], hex.EncodeToString(react.data))
					break SELECT
				}
				rp, err := react.data.GetPDU()
				if err != nil {
					if err == ErrorCrc {
						atomic.AddInt64(&c.com.Stats().CrcErrors, 1)
					} else {
						atomic.AddInt64(&c.com.Stats().OtherErrors, 1)
					}
					act.errChan <- err
					break READ_LOOP
				}
				hasErr, fc := rp.GetFunctionCode().SeparateError()
				if hasErr && fc == afc {
					atomic.AddInt64(&c.com.Stats().RemoteErrors, 1)
					handler.OnError(ap, rp)
					act.errChan <- fmt.Errorf("server reply with exception:%v", hex.EncodeToString(rp))
					break READ_LOOP
				}
				if !IsRequestReply(act.data.fastGetPDU(), rp) {
					readUnexpected(act, func() {
						atomic.AddInt64(&c.com.Stats().OtherErrors, 1)
					})
					act.errChan <- fmt.Errorf("unexpected reply:%v", hex.EncodeToString(rp))
					break READ_LOOP
				}
				if afc.IsReadToServer() {
					//read from server, write here
					bs, err := rp.GetReplyValues()
					if err != nil {
						atomic.AddInt64(&c.com.Stats().OtherErrors, 1)
						act.errChan <- err
						break READ_LOOP
					}
					err = handler.OnWrite(ap, bs)
					if err != nil {
						atomic.AddInt64(&c.com.Stats().OtherErrors, 1)
					}
					act.errChan <- err //success if nil
					break READ_LOOP
				}
				act.errChan <- nil //success
				break READ_LOOP
			}
		}
	}
}

//Close closes the client and closes the connect
func (c *FailoverRTUClient) Close() error {
	return c.com.Close()
}

//DoTransaction starts a transaction, and returns a channel that returns an error
//or nil, with the default slaveID.
//
//DoTransaction is blocking.
//
//For read from server, the PDU is sent as is (after been warped up in RTU)
//For write to server, the data part given will be ignored, and filled in by data from handler.
func (c *FailoverRTUClient) DoTransaction(req PDU) error {
	errChan := make(chan error)
	c.StartTransactionToServer(c.SlaveID, req, errChan)
	return <-errChan
}

//StartTransactionToServer starts a transaction, with a custom slaveID.
//errChan is required and usable, an error is set is the transaction failed, or
//nil for success.
//
//StartTransactionToServer is not blocking.
//
//For read from server, the PDU is sent as is (after been warped up in RTU)
//For write to server, the data part given will be ignored, and filled in by data from handler.
func (c *FailoverRTUClient) StartTransactionToServer(slaveID byte, req PDU, errChan chan error) {
	c.actions <- rtuAction{t: clientStart, data: MakeRTU(slaveID, req), errChan: errChan}
}
<|endoftext|>