<commit_before>package cmd

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"

	"github.com/minamijoyo/myaws/myaws/autoscaling"
)

func init() {
	RootCmd.AddCommand(newAutoscalingCmd())
}

func newAutoscalingCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "autoscaling",
		Short: "Manage autoscaling resources",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Help()
		},
	}

	cmd.AddCommand(
		newAutoscalingLsCmd(),
		newAutoscalingAttachCmd(),
		newAutoscalingDetachCmd(),
		newAutoscalingUpdateCmd(),
	)

	return cmd
}

func newAutoscalingLsCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "ls",
		Short: "List autoscaling groups",
		RunE:  runAutoscalingLsCmd,
	}

	flags := cmd.Flags()
	flags.BoolP("all", "a", false, "List all autoscaling groups (by default, list autoscaling groups only having at least 1 attached instance)")

	viper.BindPFlag("autoscaling.ls.all", flags.Lookup("all"))

	return cmd
}

func runAutoscalingLsCmd(cmd *cobra.Command, args []string) error {
	client, err := newClient()
	if err != nil {
		return errors.Wrap(err, "newClient failed:")
	}

	options := autoscaling.LsOptions{
		All: viper.GetBool("autoscaling.ls.all"),
	}

	return autoscaling.Ls(client, options)
}

func newAutoscalingAttachCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "attach AUTO_SCALING_GROUP_NAME",
		Short: "Attach instances/loadbalancers to autoscaling group",
		RunE:  runAutoscalingAttachCmd,
	}

	flags := cmd.Flags()
	flags.StringP("instance-ids", "i", "", "One or more instance IDs")
	flags.StringP("load-balancer-names", "l", "", "One or more load balancer names")

	viper.BindPFlag("autoscaling.attach.instance-ids", flags.Lookup("instance-ids"))
	viper.BindPFlag("autoscaling.attach.load-balancer-names", flags.Lookup("load-balancer-names"))

	return cmd
}

func runAutoscalingAttachCmd(cmd *cobra.Command, args []string) error {
	client, err := newClient()
	if err != nil {
		return errors.Wrap(err, "newClient failed:")
	}

	if len(args) == 0 {
		return errors.New("AUTO_SCALING_GROUP_NAME is required")
	}

	instanceIds := aws.StringSlice(viper.GetStringSlice("autoscaling.attach.instance-ids"))
	loadBalancerNames := aws.StringSlice(viper.GetStringSlice("autoscaling.attach.load-balancer-names"))
	options := autoscaling.AttachOptions{
		AsgName:           args[0],
		InstanceIds:       instanceIds,
		LoadBalancerNames: loadBalancerNames,
	}

	return autoscaling.Attach(client, options)
}

func newAutoscalingDetachCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "detach AUTO_SCALING_GROUP_NAME",
		Short: "Detach instances/loadbalancers from autoscaling group",
		RunE:  runAutoscalingDetachCmd,
	}

	flags := cmd.Flags()
	flags.StringP("instance-ids", "i", "", "One or more instance IDs")
	flags.StringP("load-balancer-names", "l", "", "One or more load balancer names")

	viper.BindPFlag("autoscaling.detach.instance-ids", flags.Lookup("instance-ids"))
	viper.BindPFlag("autoscaling.detach.load-balancer-names", flags.Lookup("load-balancer-names"))

	return cmd
}

func runAutoscalingDetachCmd(cmd *cobra.Command, args []string) error {
	client, err := newClient()
	if err != nil {
		return errors.Wrap(err, "newClient failed:")
	}

	if len(args) == 0 {
		return errors.New("AUTO_SCALING_GROUP_NAME is required")
	}

	instanceIds := aws.StringSlice(viper.GetStringSlice("autoscaling.detach.instance-ids"))
	loadBalancerNames := aws.StringSlice(viper.GetStringSlice("autoscaling.detach.load-balancer-names"))
	options := autoscaling.DetachOptions{
		AsgName:           args[0],
		InstanceIds:       instanceIds,
		LoadBalancerNames: loadBalancerNames,
	}

	return autoscaling.Detach(client, options)
}

func newAutoscalingUpdateCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "update AUTO_SCALING_GROUP_NAME",
		Short: "Update autoscaling group",
		RunE:  runAutoscalingUpdateCmd,
	}

	flags := cmd.Flags()
	flags.Int64P("desired-capacity", "c", -1, "The number of EC2 instances that should be running in the Auto Scaling group.")

	viper.BindPFlag("autoscaling.update.desired-capacity", flags.Lookup("desired-capacity"))

	return cmd
}

func runAutoscalingUpdateCmd(cmd *cobra.Command, args []string) error {
	client, err := newClient()
	if err != nil {
		return errors.Wrap(err, "newClient failed:")
	}

	if len(args) == 0 {
		return errors.New("AUTO_SCALING_GROUP_NAME is required")
	}

	desiredCapacity := viper.GetInt64("autoscaling.update.desired-capacity")
	if desiredCapacity == -1 {
		return errors.New("--desired-capacity is required")
	}

	options := autoscaling.UpdateOptions{
		AsgName:         args[0],
		DesiredCapacity: desiredCapacity,
	}

	return autoscaling.Update(client, options)
}
<commit_msg>Set `as` to an alias for the `autoscaling` command<commit_after>package cmd

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"

	"github.com/minamijoyo/myaws/myaws/autoscaling"
)

func init() {
	RootCmd.AddCommand(newAutoscalingCmd())
}

func newAutoscalingCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:     "autoscaling",
		Aliases: []string{"as"},
		Short:   "Manage autoscaling resources",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Help()
		},
	}

	cmd.AddCommand(
		newAutoscalingLsCmd(),
		newAutoscalingAttachCmd(),
		newAutoscalingDetachCmd(),
		newAutoscalingUpdateCmd(),
	)

	return cmd
}

func newAutoscalingLsCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "ls",
		Short: "List autoscaling groups",
		RunE:  runAutoscalingLsCmd,
	}

	flags := cmd.Flags()
	flags.BoolP("all", "a", false, "List all autoscaling groups (by default, list autoscaling groups only having at least 1 attached instance)")

	viper.BindPFlag("autoscaling.ls.all", flags.Lookup("all"))

	return cmd
}

func runAutoscalingLsCmd(cmd *cobra.Command, args []string) error {
	client, err := newClient()
	if err != nil {
		return errors.Wrap(err, "newClient failed:")
	}

	options := autoscaling.LsOptions{
		All: viper.GetBool("autoscaling.ls.all"),
	}

	return autoscaling.Ls(client, options)
}

func newAutoscalingAttachCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "attach AUTO_SCALING_GROUP_NAME",
		Short: "Attach instances/loadbalancers to autoscaling group",
		RunE:  runAutoscalingAttachCmd,
	}

	flags := cmd.Flags()
	flags.StringP("instance-ids", "i", "", "One or more instance IDs")
	flags.StringP("load-balancer-names", "l", "", "One or more load balancer names")

	viper.BindPFlag("autoscaling.attach.instance-ids", flags.Lookup("instance-ids"))
	viper.BindPFlag("autoscaling.attach.load-balancer-names", flags.Lookup("load-balancer-names"))

	return cmd
}

func runAutoscalingAttachCmd(cmd *cobra.Command, args []string) error {
	client, err := newClient()
	if err != nil {
		return errors.Wrap(err, "newClient failed:")
	}

	if len(args) == 0 {
		return errors.New("AUTO_SCALING_GROUP_NAME is required")
	}

	instanceIds := aws.StringSlice(viper.GetStringSlice("autoscaling.attach.instance-ids"))
	loadBalancerNames := aws.StringSlice(viper.GetStringSlice("autoscaling.attach.load-balancer-names"))
	options := autoscaling.AttachOptions{
		AsgName:           args[0],
		InstanceIds:       instanceIds,
		LoadBalancerNames: loadBalancerNames,
	}

	return autoscaling.Attach(client, options)
}

func newAutoscalingDetachCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "detach AUTO_SCALING_GROUP_NAME",
		Short: "Detach instances/loadbalancers from autoscaling group",
		RunE:  runAutoscalingDetachCmd,
	}

	flags := cmd.Flags()
	flags.StringP("instance-ids", "i", "", "One or more instance IDs")
	flags.StringP("load-balancer-names", "l", "", "One or more load balancer names")

	viper.BindPFlag("autoscaling.detach.instance-ids", flags.Lookup("instance-ids"))
	viper.BindPFlag("autoscaling.detach.load-balancer-names", flags.Lookup("load-balancer-names"))

	return cmd
}

func runAutoscalingDetachCmd(cmd *cobra.Command, args []string) error {
	client, err := newClient()
	if err != nil {
		return errors.Wrap(err, "newClient failed:")
	}

	if len(args) == 0 {
		return errors.New("AUTO_SCALING_GROUP_NAME is required")
	}

	instanceIds := aws.StringSlice(viper.GetStringSlice("autoscaling.detach.instance-ids"))
	loadBalancerNames := aws.StringSlice(viper.GetStringSlice("autoscaling.detach.load-balancer-names"))
	options := autoscaling.DetachOptions{
		AsgName:           args[0],
		InstanceIds:       instanceIds,
		LoadBalancerNames: loadBalancerNames,
	}

	return autoscaling.Detach(client, options)
}

func newAutoscalingUpdateCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "update AUTO_SCALING_GROUP_NAME",
		Short: "Update autoscaling group",
		RunE:  runAutoscalingUpdateCmd,
	}

	flags := cmd.Flags()
	flags.Int64P("desired-capacity", "c", -1, "The number of EC2 instances that should be running in the Auto Scaling group.")

	viper.BindPFlag("autoscaling.update.desired-capacity", flags.Lookup("desired-capacity"))

	return cmd
}

func runAutoscalingUpdateCmd(cmd *cobra.Command, args []string) error {
	client, err := newClient()
	if err != nil {
		return errors.Wrap(err, "newClient failed:")
	}

	if len(args) == 0 {
		return errors.New("AUTO_SCALING_GROUP_NAME is required")
	}

	desiredCapacity := viper.GetInt64("autoscaling.update.desired-capacity")
	if desiredCapacity == -1 {
		return errors.New("--desired-capacity is required")
	}

	options := autoscaling.UpdateOptions{
		AsgName:         args[0],
		DesiredCapacity: desiredCapacity,
	}

	return autoscaling.Update(client, options)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path"
	"runtime"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/tendermint/tmlibs/cli"

	"github.com/bytom/cmd/bytomd/commands"
)

// ContextHook is a hook for logrus.
type ContextHook struct{}

// Levels returns the whole levels.
func (hook ContextHook) Levels() []log.Level {
	return log.AllLevels
}

// Fire helps logrus record the related file, function and line.
func (hook ContextHook) Fire(entry *log.Entry) error {
	pc := make([]uintptr, 3, 3)
	cnt := runtime.Callers(6, pc)

	for i := 0; i < cnt; i++ {
		fu := runtime.FuncForPC(pc[i] - 1)
		name := fu.Name()
		if !strings.Contains(name, "github.com/Sirupsen/log") {
			file, line := fu.FileLine(pc[i] - 1)
			entry.Data["file"] = path.Base(file)
			entry.Data["func"] = path.Base(name)
			entry.Data["line"] = line
			break
		}
	}
	return nil
}

func init() {
	log.SetFormatter(&log.TextFormatter{FullTimestamp: true})

	// If environment variable BYTOM_DEBUG is not empty,
	// then add the hook to logrus and set the log level to DEBUG
	if os.Getenv("BYTOM_DEBUG") != "" {
		log.AddHook(ContextHook{})
		log.SetLevel(log.DebugLevel)
	}
}

func main() {
	cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv("./.bytomd"))
	cmd.Execute()
}
<commit_msg>Disable colored logging<commit_after>package main

import (
	"os"
	"path"
	"runtime"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/tendermint/tmlibs/cli"

	"github.com/bytom/cmd/bytomd/commands"
)

// ContextHook is a hook for logrus.
type ContextHook struct{}

// Levels returns the whole levels.
func (hook ContextHook) Levels() []log.Level {
	return log.AllLevels
}

// Fire helps logrus record the related file, function and line.
func (hook ContextHook) Fire(entry *log.Entry) error {
	pc := make([]uintptr, 3, 3)
	cnt := runtime.Callers(6, pc)

	for i := 0; i < cnt; i++ {
		fu := runtime.FuncForPC(pc[i] - 1)
		name := fu.Name()
		if !strings.Contains(name, "github.com/Sirupsen/log") {
			file, line := fu.FileLine(pc[i] - 1)
			entry.Data["file"] = path.Base(file)
			entry.Data["func"] = path.Base(name)
			entry.Data["line"] = line
			break
		}
	}
	return nil
}

func init() {
	log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableColors: true})

	// If environment variable BYTOM_DEBUG is not empty,
	// then add the hook to logrus and set the log level to DEBUG
	if os.Getenv("BYTOM_DEBUG") != "" {
		log.AddHook(ContextHook{})
		log.SetLevel(log.DebugLevel)
	}
}

func main() {
	cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv("./.bytomd"))
	cmd.Execute()
}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google 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 main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/cmdmain"
	"camlistore.org/pkg/jsonsign"
	"camlistore.org/pkg/osutil"
)

type initCmd struct {
	newKey bool
	gpgkey string
	noconfig bool
}

func init() {
	cmdmain.RegisterCommand("init", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := new(initCmd)
		flags.BoolVar(&cmd.newKey, "newkey", false, "Automatically generate a new identity in a new secret ring.")
		flags.StringVar(&cmd.gpgkey, "gpgkey", "", "GPG key to use for signing (overrides $GPGKEY environment)")
		flags.BoolVar(&cmd.noconfig, "noconfig", false, "Stop after creating the public key blob, and do not try and create a config file.")
		return cmd
	})
}

func (c *initCmd) Describe() string {
	return "Initialize the camput configuration file. With no option, it tries to use the GPG key found in the default identity secret ring."
}

func (c *initCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: camput init [opts]")
}

func (c *initCmd) Examples() []string {
	return []string{
		"",
		"--gpgkey=XXXXX",
		"--newkey Creates a new identity",
	}
}

// keyId returns the current keyId. It checks, in this order,
// the --gpgkey flag, the GPGKEY env var, and the default
// identity secret ring.
func (c *initCmd) keyId(secRing string) (string, error) {
	if k := c.gpgkey; k != "" {
		return k, nil
	}
	if k := os.Getenv("GPGKEY"); k != "" {
		return k, nil
	}

	k, err := jsonsign.KeyIdFromRing(secRing)
	if err != nil {
		log.Printf("No suitable gpg key was found in %v: %v", secRing, err)
	} else {
		if k != "" {
			log.Printf("Re-using identity with keyId %q found in file %s", k, secRing)
			return k, nil
		}
	}

	// TODO: run and parse gpg --list-secret-keys and see if there's just one and suggest that?  Or show
	// a list of them?
	return "", errors.New("Initialization requires your public GPG key.\nYou can set --gpgkey=<pubid> or set $GPGKEY in your environment. Run gpg --list-secret-keys to find their key IDs.\nOr you can create a new secret ring and key with 'camput init --newkey'.")
}

func (c *initCmd) getPublicKeyArmoredFromFile(secretRingFileName, keyId string) (b []byte, err error) {
	entity, err := jsonsign.EntityFromSecring(keyId, secretRingFileName)
	if err == nil {
		pubArmor, err := jsonsign.ArmoredPublicKey(entity)
		if err == nil {
			return []byte(pubArmor), nil
		}
	}
	b, err = exec.Command("gpg", "--export", "--armor", keyId).Output()
	if err != nil {
		return nil, fmt.Errorf("Error running gpg to export public key %q: %v", keyId, err)
	}
	if len(b) == 0 {
		return nil, fmt.Errorf("gpg export of public key %q was empty.", keyId)
	}
	return b, nil
}

func (c *initCmd) getPublicKeyArmored(keyId string) (b []byte, err error) {
	files := []string{osutil.IdentitySecretRing(), jsonsign.DefaultSecRingPath()}
	for _, file := range files {
		b, err = c.getPublicKeyArmoredFromFile(file, keyId)
		if err == nil {
			return b, nil
		}
	}
	return nil, fmt.Errorf("failed to export armored public key ID %q from locations: %q", keyId, files)
}

func (c *initCmd) RunCommand(args []string) error {
	if len(args) > 0 {
		return cmdmain.ErrUsage
	}

	if c.newKey && c.gpgkey != "" {
		log.Fatal("--newkey and --gpgkey are mutually exclusive")
	}

	blobDir := path.Join(osutil.CamliConfigDir(), "keyblobs")
	os.Mkdir(osutil.CamliConfigDir(), 0700)
	os.Mkdir(blobDir, 0700)

	var keyId string
	var err error
	secRing := osutil.IdentitySecretRing()
	if c.newKey {
		keyId, err = jsonsign.GenerateNewSecRing(secRing)
		if err != nil {
			return err
		}
	} else {
		keyId, err = c.keyId(secRing)
		if err != nil {
			return err
		}
	}

	if os.Getenv("GPG_AGENT_INFO") == "" {
		log.Printf("No GPG_AGENT_INFO found in environment; you should setup gnupg-agent.  camput might be annoying otherwise, if your private key is encrypted.")
	}

	pubArmor, err := c.getPublicKeyArmored(keyId)
	if err != nil {
		return err
	}

	bref := blob.SHA1FromString(string(pubArmor))

	keyBlobPath := path.Join(blobDir, bref.String()+".camli")
	if err = ioutil.WriteFile(keyBlobPath, pubArmor, 0644); err != nil {
		log.Fatalf("Error writing public key blob to %q: %v", keyBlobPath, err)
	}

	if ok, err := jsonsign.VerifyPublicKeyFile(keyBlobPath, keyId); !ok {
		log.Fatalf("Error verifying public key at %q: %v", keyBlobPath, err)
	}

	log.Printf("Your Camlistore identity (your GPG public key's blobref) is: %s", bref.String())

	if c.noconfig {
		return nil
	}

	configFilePath := osutil.UserClientConfigPath()
	_, err = os.Stat(configFilePath)
	if err == nil {
		log.Fatalf("Config file %q already exists; quitting without touching it.", configFilePath)
	}

	if f, err := os.OpenFile(configFilePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600); err == nil {
		defer f.Close()
		m := make(map[string]interface{})
		m["keyId"] = keyId                    // TODO(bradfitz): make this 'identity' to match server config?
		m["publicKeyBlobref"] = bref.String() // TODO(bradfitz): not used anymore?
		m["server"] = "http://localhost:3179/"
		m["selfPubKeyDir"] = blobDir
		m["auth"] = "localhost"
		m["ignoredFiles"] = []string{".DS_Store"}

		jsonBytes, err := json.MarshalIndent(m, "", "  ")
		if err != nil {
			log.Fatalf("JSON serialization error: %v", err)
		}
		_, err = f.Write(jsonBytes)
		if err != nil {
			log.Fatalf("Error writing to %q: %v", configFilePath, err)
		}
		log.Printf("Wrote %q; modify as necessary.", configFilePath)
	}
	return nil
}
<commit_msg>camput init: use osutil.KeyBlobsDir()<commit_after>/*
Copyright 2011 Google 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 main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/cmdmain"
	"camlistore.org/pkg/jsonsign"
	"camlistore.org/pkg/osutil"
)

type initCmd struct {
	newKey   bool
	gpgkey   string
	noconfig bool
}

func init() {
	cmdmain.RegisterCommand("init", func(flags *flag.FlagSet) cmdmain.CommandRunner {
		cmd := new(initCmd)
		flags.BoolVar(&cmd.newKey, "newkey", false, "Automatically generate a new identity in a new secret ring.")
		flags.StringVar(&cmd.gpgkey, "gpgkey", "", "GPG key to use for signing (overrides $GPGKEY environment)")
		flags.BoolVar(&cmd.noconfig, "noconfig", false, "Stop after creating the public key blob, and do not try and create a config file.")
		return cmd
	})
}

func (c *initCmd) Describe() string {
	return "Initialize the camput configuration file. With no option, it tries to use the GPG key found in the default identity secret ring."
}

func (c *initCmd) Usage() {
	fmt.Fprintf(cmdmain.Stderr, "Usage: camput init [opts]")
}

func (c *initCmd) Examples() []string {
	return []string{
		"",
		"--gpgkey=XXXXX",
		"--newkey Creates a new identity",
	}
}

// keyId returns the current keyId. It checks, in this order,
// the --gpgkey flag, the GPGKEY env var, and the default
// identity secret ring.
func (c *initCmd) keyId(secRing string) (string, error) {
	if k := c.gpgkey; k != "" {
		return k, nil
	}
	if k := os.Getenv("GPGKEY"); k != "" {
		return k, nil
	}

	k, err := jsonsign.KeyIdFromRing(secRing)
	if err != nil {
		log.Printf("No suitable gpg key was found in %v: %v", secRing, err)
	} else {
		if k != "" {
			log.Printf("Re-using identity with keyId %q found in file %s", k, secRing)
			return k, nil
		}
	}

	// TODO: run and parse gpg --list-secret-keys and see if there's just one and suggest that?  Or show
	// a list of them?
	return "", errors.New("Initialization requires your public GPG key.\nYou can set --gpgkey=<pubid> or set $GPGKEY in your environment. Run gpg --list-secret-keys to find their key IDs.\nOr you can create a new secret ring and key with 'camput init --newkey'.")
}

func (c *initCmd) getPublicKeyArmoredFromFile(secretRingFileName, keyId string) (b []byte, err error) {
	entity, err := jsonsign.EntityFromSecring(keyId, secretRingFileName)
	if err == nil {
		pubArmor, err := jsonsign.ArmoredPublicKey(entity)
		if err == nil {
			return []byte(pubArmor), nil
		}
	}
	b, err = exec.Command("gpg", "--export", "--armor", keyId).Output()
	if err != nil {
		return nil, fmt.Errorf("Error running gpg to export public key %q: %v", keyId, err)
	}
	if len(b) == 0 {
		return nil, fmt.Errorf("gpg export of public key %q was empty.", keyId)
	}
	return b, nil
}

func (c *initCmd) getPublicKeyArmored(keyId string) (b []byte, err error) {
	files := []string{osutil.IdentitySecretRing(), jsonsign.DefaultSecRingPath()}
	for _, file := range files {
		b, err = c.getPublicKeyArmoredFromFile(file, keyId)
		if err == nil {
			return b, nil
		}
	}
	return nil, fmt.Errorf("failed to export armored public key ID %q from locations: %q", keyId, files)
}

func (c *initCmd) RunCommand(args []string) error {
	if len(args) > 0 {
		return cmdmain.ErrUsage
	}

	if c.newKey && c.gpgkey != "" {
		log.Fatal("--newkey and --gpgkey are mutually exclusive")
	}

	blobDir := osutil.KeyBlobsDir()
	if err := os.MkdirAll(blobDir, 0700); err != nil {
		return err
	}

	var keyId string
	var err error
	secRing := osutil.IdentitySecretRing()
	if c.newKey {
		keyId, err = jsonsign.GenerateNewSecRing(secRing)
		if err != nil {
			return err
		}
	} else {
		keyId, err = c.keyId(secRing)
		if err != nil {
			return err
		}
	}

	if os.Getenv("GPG_AGENT_INFO") == "" {
		log.Printf("No GPG_AGENT_INFO found in environment; you should setup gnupg-agent.  camput might be annoying otherwise, if your private key is encrypted.")
	}

	pubArmor, err := c.getPublicKeyArmored(keyId)
	if err != nil {
		return err
	}

	bref := blob.SHA1FromString(string(pubArmor))

	keyBlobPath := path.Join(blobDir, bref.String()+".camli")
	if err = ioutil.WriteFile(keyBlobPath, pubArmor, 0644); err != nil {
		log.Fatalf("Error writing public key blob to %q: %v", keyBlobPath, err)
	}

	if ok, err := jsonsign.VerifyPublicKeyFile(keyBlobPath, keyId); !ok {
		log.Fatalf("Error verifying public key at %q: %v", keyBlobPath, err)
	}

	log.Printf("Your Camlistore identity (your GPG public key's blobref) is: %s", bref.String())

	if c.noconfig {
		return nil
	}

	configFilePath := osutil.UserClientConfigPath()
	_, err = os.Stat(configFilePath)
	if err == nil {
		log.Fatalf("Config file %q already exists; quitting without touching it.", configFilePath)
	}

	if f, err := os.OpenFile(configFilePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600); err == nil {
		defer f.Close()
		m := make(map[string]interface{})
		m["keyId"] = keyId                    // TODO(bradfitz): make this 'identity' to match server config?
		m["publicKeyBlobref"] = bref.String() // TODO(bradfitz): not used anymore?
		m["server"] = "http://localhost:3179/"
		m["selfPubKeyDir"] = blobDir
		m["auth"] = "localhost"
		m["ignoredFiles"] = []string{".DS_Store"}

		jsonBytes, err := json.MarshalIndent(m, "", "  ")
		if err != nil {
			log.Fatalf("JSON serialization error: %v", err)
		}
		_, err = f.Write(jsonBytes)
		if err != nil {
			log.Fatalf("Error writing to %q: %v", configFilePath, err)
		}
		log.Printf("Wrote %q; modify as necessary.", configFilePath)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
 * MinIO Cloud Storage, (C) 2017-2019 MinIO, 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 cmd

import (
	"crypto/x509"
	"errors"
	"net"
	"path/filepath"
	"strings"
	"time"

	dns2 "github.com/miekg/dns"
	"github.com/minio/cli"
	"github.com/minio/minio-go/v6/pkg/set"
	"github.com/minio/minio/cmd/config"
	"github.com/minio/minio/cmd/config/etcd"
	"github.com/minio/minio/cmd/logger"
	"github.com/minio/minio/pkg/auth"
	"github.com/minio/minio/pkg/certs"
	"github.com/minio/minio/pkg/dns"
	"github.com/minio/minio/pkg/env"
)

func verifyObjectLayerFeatures(name string, objAPI ObjectLayer) {
	if (globalAutoEncryption || GlobalKMS != nil) && !objAPI.IsEncryptionSupported() {
		logger.Fatal(errInvalidArgument,
			"Encryption support is requested but '%s' does not support encryption", name)
	}

	if strings.HasPrefix(name, "gateway") {
		if GlobalGatewaySSE.IsSet() && GlobalKMS == nil {
			uiErr := config.ErrInvalidGWSSEEnvValue(nil).Msg("MINIO_GATEWAY_SSE set but KMS is not configured")
			logger.Fatal(uiErr, "Unable to start gateway with SSE")
		}
	}

	if globalIsCompressionEnabled && !objAPI.IsCompressionSupported() {
		logger.Fatal(errInvalidArgument,
			"Compression support is requested but '%s' does not support compression", name)
	}
}

// Check for updates and print a notification message
func checkUpdate(mode string) {
	// Its OK to ignore any errors during doUpdate() here.
	if updateMsg, _, currentReleaseTime, latestReleaseTime, err := getUpdateInfo(2*time.Second, mode); err == nil {
		if updateMsg == "" {
			return
		}
		if globalInplaceUpdateDisabled {
			logStartupMessage(updateMsg)
		} else {
			logStartupMessage(prepareUpdateMessage("Run `mc admin update`", latestReleaseTime.Sub(currentReleaseTime)))
		}
	}
}

func newConfigDirFromCtx(ctx *cli.Context, option string, getDefaultDir func() string) (*ConfigDir, bool) {
	var dir string
	var dirSet bool

	switch {
	case ctx.IsSet(option):
		dir = ctx.String(option)
		dirSet = true
	case ctx.GlobalIsSet(option):
		dir = ctx.GlobalString(option)
		dirSet = true
		// cli package does not expose parent's option option.  Below code is workaround.
		if dir == "" || dir == getDefaultDir() {
			dirSet = false // Unset to false since GlobalIsSet() true is a false positive.
			if ctx.Parent().GlobalIsSet(option) {
				dir = ctx.Parent().GlobalString(option)
				dirSet = true
			}
		}
	default:
		// Neither local nor global option is provided.  In this case, try to use
		// default directory.
		dir = getDefaultDir()
		if dir == "" {
			logger.FatalIf(errInvalidArgument, "%s option must be provided", option)
		}
	}

	if dir == "" {
		logger.FatalIf(errors.New("empty directory"), "%s directory cannot be empty", option)
	}

	// Disallow relative paths, figure out absolute paths.
	dirAbs, err := filepath.Abs(dir)
	logger.FatalIf(err, "Unable to fetch absolute path for %s=%s", option, dir)

	logger.FatalIf(mkdirAllIgnorePerm(dirAbs), "Unable to create directory specified %s=%s", option, dir)

	return &ConfigDir{path: dirAbs}, dirSet
}

func handleCommonCmdArgs(ctx *cli.Context) {

	// Get "json" flag from command line argument and
	// enable json and quite modes if json flag is turned on.
	globalCLIContext.JSON = ctx.IsSet("json") || ctx.GlobalIsSet("json")
	if globalCLIContext.JSON {
		logger.EnableJSON()
	}

	// Get quiet flag from command line argument.
	globalCLIContext.Quiet = ctx.IsSet("quiet") || ctx.GlobalIsSet("quiet")
	if globalCLIContext.Quiet {
		logger.EnableQuiet()
	}

	// Get anonymous flag from command line argument.
	globalCLIContext.Anonymous = ctx.IsSet("anonymous") || ctx.GlobalIsSet("anonymous")
	if globalCLIContext.Anonymous {
		logger.EnableAnonymous()
	}

	// Fetch address option
	globalCLIContext.Addr = ctx.GlobalString("address")
	if globalCLIContext.Addr == "" || globalCLIContext.Addr == ":"+globalMinioDefaultPort {
		globalCLIContext.Addr = ctx.String("address")
	}

	// Set all config, certs and CAs directories.
	var configSet, certsSet bool
	globalConfigDir, configSet = newConfigDirFromCtx(ctx, "config-dir", defaultConfigDir.Get)
	globalCertsDir, certsSet = newConfigDirFromCtx(ctx, "certs-dir", defaultCertsDir.Get)

	// Remove this code when we deprecate and remove config-dir.
	// This code is to make sure we inherit from the config-dir
	// option if certs-dir is not provided.
	if !certsSet && configSet {
		globalCertsDir = &ConfigDir{path: filepath.Join(globalConfigDir.Get(), certsDir)}
	}

	globalCertsCADir = &ConfigDir{path: filepath.Join(globalCertsDir.Get(), certsCADir)}

	logger.FatalIf(mkdirAllIgnorePerm(globalCertsCADir.Get()), "Unable to create certs CA directory at %s", globalCertsCADir.Get())

	// Check "compat" flag from command line argument.
	globalCLIContext.StrictS3Compat = ctx.IsSet("compat") || ctx.GlobalIsSet("compat")
}

func handleCommonEnvVars() {
	accessKey := env.Get(config.EnvAccessKey, "")
	secretKey := env.Get(config.EnvSecretKey, "")
	if accessKey != "" && secretKey != "" {
		cred, err := auth.CreateCredentials(accessKey, secretKey)
		if err != nil {
			logger.Fatal(config.ErrInvalidCredentials(err), "Unable to validate credentials inherited from the shell environment")
		}
		cred.Expiration = timeSentinel

		// credential Envs are set globally.
		globalIsEnvCreds = true
		globalActiveCred = cred
	}

	if browser := env.Get(config.EnvBrowser, "on"); browser != "" {
		browserFlag, err := config.ParseBoolFlag(browser)
		if err != nil {
			logger.Fatal(config.ErrInvalidBrowserValue(nil).Msg("Unknown value `%s`", browser), "Invalid MINIO_BROWSER value in environment variable")
		}

		// browser Envs are set globally, this does not represent
		// if browser is turned off or on.
		globalIsEnvBrowser = true
		globalIsBrowserEnabled = bool(browserFlag)
	}

	var err error
	globalEtcdClient, err = etcd.New(globalRootCAs)
	if err != nil {
		logger.FatalIf(err, "Unable to initialize etcd config")
	}

	for _, domainName := range strings.Split(env.Get(config.EnvDomain, ""), ",") {
		if domainName != "" {
			if _, ok := dns2.IsDomainName(domainName); !ok {
				logger.Fatal(config.ErrInvalidDomainValue(nil).Msg("Unknown value `%s`", domainName),
					"Invalid MINIO_DOMAIN value in environment variable")
			}
			globalDomainNames = append(globalDomainNames, domainName)
		}
	}

	minioEndpointsEnv, ok := env.Lookup(config.EnvPublicIPs)
	if ok {
		minioEndpoints := strings.Split(minioEndpointsEnv, ",")
		var domainIPs = set.NewStringSet()
		for _, endpoint := range minioEndpoints {
			if net.ParseIP(endpoint) == nil {
				// Checking if the IP is a DNS entry.
				addrs, err := net.LookupHost(endpoint)
				if err != nil {
					logger.FatalIf(err, "Unable to initialize MinIO server with [%s] invalid entry found in MINIO_PUBLIC_IPS", endpoint)
				}
				for _, addr := range addrs {
					domainIPs.Add(addr)
				}
				continue
			}
			domainIPs.Add(endpoint)
		}
		updateDomainIPs(domainIPs)
	} else {
		// Add found interfaces IP address to global domain IPS,
		// loopback addresses will be naturally dropped.
		updateDomainIPs(localIP4)
	}

	if len(globalDomainNames) != 0 && !globalDomainIPs.IsEmpty() && globalEtcdClient != nil {
		var err error
		globalDNSConfig, err = dns.NewCoreDNS(globalDomainNames, globalDomainIPs, globalMinioPort, globalEtcdClient)
		logger.FatalIf(err, "Unable to initialize DNS config for %s.", globalDomainNames)
	}

	// In place update is true by default if the MINIO_UPDATE is not set
	// or is not set to 'off', if MINIO_UPDATE is set to 'off' then
	// in-place update is off.
	globalInplaceUpdateDisabled = strings.EqualFold(env.Get(config.EnvUpdate, "off"), "off")

	// Get WORM environment variable.
	if worm := env.Get(config.EnvWorm, "off"); worm != "" {
		wormFlag, err := config.ParseBoolFlag(worm)
		if err != nil {
			logger.Fatal(config.ErrInvalidWormValue(nil).Msg("Unknown value `%s`", worm), "Invalid MINIO_WORM value in environment variable")
		}

		// worm Envs are set globally, this does not represent
		// if worm is turned off or on.
		globalIsEnvWORM = true
		globalWORMEnabled = bool(wormFlag)
	}

}

func logStartupMessage(msg string) {
	if globalConsoleSys != nil {
		globalConsoleSys.Send(msg, string(logger.All))
	}
	logger.StartupMessage(msg)
}

func getTLSConfig() (x509Certs []*x509.Certificate, c *certs.Certs, secureConn bool, err error) {
	if !(isFile(getPublicCertFile()) && isFile(getPrivateKeyFile())) {
		return nil, nil, false, nil
	}

	if x509Certs, err = config.ParsePublicCertFile(getPublicCertFile()); err != nil {
		return nil, nil, false, err
	}

	c, err = certs.New(getPublicCertFile(), getPrivateKeyFile(), config.LoadX509KeyPair)
	if err != nil {
		return nil, nil, false, err
	}

	secureConn = true
	return x509Certs, c, secureConn, nil
}
<commit_msg>Remote update should be on by default (#8413)<commit_after>/*
 * MinIO Cloud Storage, (C) 2017-2019 MinIO, 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 cmd

import (
	"crypto/x509"
	"errors"
	"net"
	"path/filepath"
	"strings"
	"time"

	dns2 "github.com/miekg/dns"
	"github.com/minio/cli"
	"github.com/minio/minio-go/v6/pkg/set"
	"github.com/minio/minio/cmd/config"
	"github.com/minio/minio/cmd/config/etcd"
	"github.com/minio/minio/cmd/logger"
	"github.com/minio/minio/pkg/auth"
	"github.com/minio/minio/pkg/certs"
	"github.com/minio/minio/pkg/dns"
	"github.com/minio/minio/pkg/env"
)

func verifyObjectLayerFeatures(name string, objAPI ObjectLayer) {
	if (globalAutoEncryption || GlobalKMS != nil) && !objAPI.IsEncryptionSupported() {
		logger.Fatal(errInvalidArgument,
			"Encryption support is requested but '%s' does not support encryption", name)
	}

	if strings.HasPrefix(name, "gateway") {
		if GlobalGatewaySSE.IsSet() && GlobalKMS == nil {
			uiErr := config.ErrInvalidGWSSEEnvValue(nil).Msg("MINIO_GATEWAY_SSE set but KMS is not configured")
			logger.Fatal(uiErr, "Unable to start gateway with SSE")
		}
	}

	if globalIsCompressionEnabled && !objAPI.IsCompressionSupported() {
		logger.Fatal(errInvalidArgument,
			"Compression support is requested but '%s' does not support compression", name)
	}
}

// Check for updates and print a notification message
func checkUpdate(mode string) {
	// Its OK to ignore any errors during doUpdate() here.
	if updateMsg, _, currentReleaseTime, latestReleaseTime, err := getUpdateInfo(2*time.Second, mode); err == nil {
		if updateMsg == "" {
			return
		}
		if globalInplaceUpdateDisabled {
			logStartupMessage(updateMsg)
		} else {
			logStartupMessage(prepareUpdateMessage("Run `mc admin update`", latestReleaseTime.Sub(currentReleaseTime)))
		}
	}
}

func newConfigDirFromCtx(ctx *cli.Context, option string, getDefaultDir func() string) (*ConfigDir, bool) {
	var dir string
	var dirSet bool

	switch {
	case ctx.IsSet(option):
		dir = ctx.String(option)
		dirSet = true
	case ctx.GlobalIsSet(option):
		dir = ctx.GlobalString(option)
		dirSet = true
		// cli package does not expose parent's option option.  Below code is workaround.
		if dir == "" || dir == getDefaultDir() {
			dirSet = false // Unset to false since GlobalIsSet() true is a false positive.
			if ctx.Parent().GlobalIsSet(option) {
				dir = ctx.Parent().GlobalString(option)
				dirSet = true
			}
		}
	default:
		// Neither local nor global option is provided.  In this case, try to use
		// default directory.
		dir = getDefaultDir()
		if dir == "" {
			logger.FatalIf(errInvalidArgument, "%s option must be provided", option)
		}
	}

	if dir == "" {
		logger.FatalIf(errors.New("empty directory"), "%s directory cannot be empty", option)
	}

	// Disallow relative paths, figure out absolute paths.
	dirAbs, err := filepath.Abs(dir)
	logger.FatalIf(err, "Unable to fetch absolute path for %s=%s", option, dir)

	logger.FatalIf(mkdirAllIgnorePerm(dirAbs), "Unable to create directory specified %s=%s", option, dir)

	return &ConfigDir{path: dirAbs}, dirSet
}

func handleCommonCmdArgs(ctx *cli.Context) {

	// Get "json" flag from command line argument and
	// enable json and quite modes if json flag is turned on.
	globalCLIContext.JSON = ctx.IsSet("json") || ctx.GlobalIsSet("json")
	if globalCLIContext.JSON {
		logger.EnableJSON()
	}

	// Get quiet flag from command line argument.
	globalCLIContext.Quiet = ctx.IsSet("quiet") || ctx.GlobalIsSet("quiet")
	if globalCLIContext.Quiet {
		logger.EnableQuiet()
	}

	// Get anonymous flag from command line argument.
	globalCLIContext.Anonymous = ctx.IsSet("anonymous") || ctx.GlobalIsSet("anonymous")
	if globalCLIContext.Anonymous {
		logger.EnableAnonymous()
	}

	// Fetch address option
	globalCLIContext.Addr = ctx.GlobalString("address")
	if globalCLIContext.Addr == "" || globalCLIContext.Addr == ":"+globalMinioDefaultPort {
		globalCLIContext.Addr = ctx.String("address")
	}

	// Set all config, certs and CAs directories.
	var configSet, certsSet bool
	globalConfigDir, configSet = newConfigDirFromCtx(ctx, "config-dir", defaultConfigDir.Get)
	globalCertsDir, certsSet = newConfigDirFromCtx(ctx, "certs-dir", defaultCertsDir.Get)

	// Remove this code when we deprecate and remove config-dir.
	// This code is to make sure we inherit from the config-dir
	// option if certs-dir is not provided.
	if !certsSet && configSet {
		globalCertsDir = &ConfigDir{path: filepath.Join(globalConfigDir.Get(), certsDir)}
	}

	globalCertsCADir = &ConfigDir{path: filepath.Join(globalCertsDir.Get(), certsCADir)}

	logger.FatalIf(mkdirAllIgnorePerm(globalCertsCADir.Get()), "Unable to create certs CA directory at %s", globalCertsCADir.Get())

	// Check "compat" flag from command line argument.
	globalCLIContext.StrictS3Compat = ctx.IsSet("compat") || ctx.GlobalIsSet("compat")
}

func handleCommonEnvVars() {
	accessKey := env.Get(config.EnvAccessKey, "")
	secretKey := env.Get(config.EnvSecretKey, "")
	if accessKey != "" && secretKey != "" {
		cred, err := auth.CreateCredentials(accessKey, secretKey)
		if err != nil {
			logger.Fatal(config.ErrInvalidCredentials(err), "Unable to validate credentials inherited from the shell environment")
		}
		cred.Expiration = timeSentinel

		// credential Envs are set globally.
		globalIsEnvCreds = true
		globalActiveCred = cred
	}

	if browser := env.Get(config.EnvBrowser, "on"); browser != "" {
		browserFlag, err := config.ParseBoolFlag(browser)
		if err != nil {
			logger.Fatal(config.ErrInvalidBrowserValue(nil).Msg("Unknown value `%s`", browser), "Invalid MINIO_BROWSER value in environment variable")
		}

		// browser Envs are set globally, this does not represent
		// if browser is turned off or on.
		globalIsEnvBrowser = true
		globalIsBrowserEnabled = bool(browserFlag)
	}

	var err error
	globalEtcdClient, err = etcd.New(globalRootCAs)
	if err != nil {
		logger.FatalIf(err, "Unable to initialize etcd config")
	}

	for _, domainName := range strings.Split(env.Get(config.EnvDomain, ""), ",") {
		if domainName != "" {
			if _, ok := dns2.IsDomainName(domainName); !ok {
				logger.Fatal(config.ErrInvalidDomainValue(nil).Msg("Unknown value `%s`", domainName),
					"Invalid MINIO_DOMAIN value in environment variable")
			}
			globalDomainNames = append(globalDomainNames, domainName)
		}
	}

	minioEndpointsEnv, ok := env.Lookup(config.EnvPublicIPs)
	if ok {
		minioEndpoints := strings.Split(minioEndpointsEnv, ",")
		var domainIPs = set.NewStringSet()
		for _, endpoint := range minioEndpoints {
			if net.ParseIP(endpoint) == nil {
				// Checking if the IP is a DNS entry.
				addrs, err := net.LookupHost(endpoint)
				if err != nil {
					logger.FatalIf(err, "Unable to initialize MinIO server with [%s] invalid entry found in MINIO_PUBLIC_IPS", endpoint)
				}
				for _, addr := range addrs {
					domainIPs.Add(addr)
				}
				continue
			}
			domainIPs.Add(endpoint)
		}
		updateDomainIPs(domainIPs)
	} else {
		// Add found interfaces IP address to global domain IPS,
		// loopback addresses will be naturally dropped.
		updateDomainIPs(localIP4)
	}

	if len(globalDomainNames) != 0 && !globalDomainIPs.IsEmpty() && globalEtcdClient != nil {
		var err error
		globalDNSConfig, err = dns.NewCoreDNS(globalDomainNames, globalDomainIPs, globalMinioPort, globalEtcdClient)
		logger.FatalIf(err, "Unable to initialize DNS config for %s.", globalDomainNames)
	}

	// In place update is true by default if the MINIO_UPDATE is not set
	// or is not set to 'off', if MINIO_UPDATE is set to 'off' then
	// in-place update is off.
	globalInplaceUpdateDisabled = strings.EqualFold(env.Get(config.EnvUpdate, "on"), "off")

	// Get WORM environment variable.
	if worm := env.Get(config.EnvWorm, "off"); worm != "" {
		wormFlag, err := config.ParseBoolFlag(worm)
		if err != nil {
			logger.Fatal(config.ErrInvalidWormValue(nil).Msg("Unknown value `%s`", worm), "Invalid MINIO_WORM value in environment variable")
		}

		// worm Envs are set globally, this does not represent
		// if worm is turned off or on.
		globalIsEnvWORM = true
		globalWORMEnabled = bool(wormFlag)
	}

}

func logStartupMessage(msg string) {
	if globalConsoleSys != nil {
		globalConsoleSys.Send(msg, string(logger.All))
	}
	logger.StartupMessage(msg)
}

func getTLSConfig() (x509Certs []*x509.Certificate, c *certs.Certs, secureConn bool, err error) {
	if !(isFile(getPublicCertFile()) && isFile(getPrivateKeyFile())) {
		return nil, nil, false, nil
	}

	if x509Certs, err = config.ParsePublicCertFile(getPublicCertFile()); err != nil {
		return nil, nil, false, err
	}

	c, err = certs.New(getPublicCertFile(), getPrivateKeyFile(), config.LoadX509KeyPair)
	if err != nil {
		return nil, nil, false, err
	}

	secureConn = true
	return x509Certs, c, secureConn, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"sort"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/urfave/cli"
	"google.golang.org/grpc"
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
	pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)

const (
	// truncatedImageIDLen is the truncated length of imageID
	truncatedIDLen = 13
)

var runtimeClient pb.RuntimeServiceClient
var imageClient pb.ImageServiceClient
var conn *grpc.ClientConn

type listOptions struct {
	// id of container or sandbox
	id string
	// podID of container
	podID string
	// Regular expression pattern to match pod name
	podNameRegexp string
	// Regular expression pattern to match the pod namespace
	podNamespaceRegexp string
	// state of the sandbox
	state string
	// show verbose info for the sandbox
	verbose bool
	// labels are selectors for the sandbox
	labels map[string]string
	// quiet is for listing just container/sandbox/image IDs
	quiet bool
	// output format
	output string
	// all containers
	all bool
	// latest container
	latest bool
	// last n containers
	last int
	// out with truncating the id
	noTrunc bool
}

type execOptions struct {
	// id of container
	id string
	// timeout to stop command
	timeout int64
	// Whether to exec a command in a tty
	tty bool
	// Whether to stream stdin
	stdin bool
	// Command to exec
	cmd []string
}
type attachOptions struct {
	// id of container
	id string
	// Whether the stdin is TTY
	tty bool
	// Whether pass Stdin to container
	stdin bool
}

type portforwardOptions struct {
	// id of sandbox
	id string
	// ports to forward
	ports []string
}

func getSortedKeys(m map[string]string) []string {
	var keys []string
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	return keys
}

func loadContainerConfig(path string) (*pb.ContainerConfig, error) {
	f, err := openFile(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var config pb.ContainerConfig
	if err := utilyaml.NewYAMLOrJSONDecoder(f, 4096).Decode(&config); err != nil {
		return nil, err
	}
	return &config, nil
}

func loadPodSandboxConfig(path string) (*pb.PodSandboxConfig, error) {
	f, err := openFile(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var config pb.PodSandboxConfig
	if err := utilyaml.NewYAMLOrJSONDecoder(f, 4096).Decode(&config); err != nil {
		return nil, err
	}
	return &config, nil
}

func openFile(path string) (*os.File, error) {
	f, err := os.Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("config at %s not found", path)
		}
		return nil, err
	}
	return f, nil
}

func getRuntimeClient(context *cli.Context) error {
	// Set up a connection to the server.
	var err error
	conn, err = getRuntimeClientConnection(context)
	if err != nil {
		return fmt.Errorf("failed to connect: %v", err)
	}
	runtimeClient = pb.NewRuntimeServiceClient(conn)
	return nil
}

func getImageClient(context *cli.Context) error {
	// Set up a connection to the server.
	var err error
	conn, err = getImageClientConnection(context)
	if err != nil {
		return fmt.Errorf("failed to connect: %v", err)
	}
	imageClient = pb.NewImageServiceClient(conn)
	return nil
}

func closeConnection(context *cli.Context) error {
	if conn == nil {
		return nil
	}

	return conn.Close()
}

func protobufObjectToJSON(obj proto.Message) (string, error) {
	jsonpbMarshaler := jsonpb.Marshaler{EmitDefaults: true, Indent: "  "}
	marshaledJSON, err := jsonpbMarshaler.MarshalToString(obj)
	if err != nil {
		return "", err
	}
	return marshaledJSON, nil
}

func outputProtobufObjAsJSON(obj proto.Message) error {
	marshaledJSON, err := protobufObjectToJSON(obj)
	if err != nil {
		return err
	}

	fmt.Println(marshaledJSON)
	return nil
}

func outputProtobufObjAsYAML(obj proto.Message) error {
	marshaledJSON, err := protobufObjectToJSON(obj)
	if err != nil {
		return err
	}
	marshaledYAML, err := yaml.JSONToYAML([]byte(marshaledJSON))
	if err != nil {
		return err
	}

	fmt.Println(string(marshaledYAML))
	return nil
}

func outputStatusInfo(status string, info map[string]string, format string) error {
	// Sort all keys
	var keys []string
	for k := range info {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	jsonInfo := "{" + "\"status\":" + status + ","
	for _, k := range keys {
		jsonInfo += "\"" + k + "\"" + ":" + info[k] + ","
	}
	jsonInfo = jsonInfo[:len(jsonInfo)-1]
	jsonInfo += "}"

	switch format {
	case "yaml":
		yamlInfo, err := yaml.JSONToYAML([]byte(jsonInfo))
		if err != nil {
			return err
		}
		fmt.Println(string(yamlInfo))
	case "json":
		var output bytes.Buffer
		if err := json.Indent(&output, []byte(jsonInfo), "", "  "); err != nil {
			return err
		}
		fmt.Println(output.String())
	default:
		fmt.Printf("Don't support %q format\n", format)
	}
	return nil
}

func parseLabelStringSlice(ss []string) (map[string]string, error) {
	labels := make(map[string]string)
	for _, s := range ss {
		pair := strings.Split(s, "=")
		if len(pair) != 2 {
			return nil, fmt.Errorf("incorrectly specified label: %v", s)
		}
		labels[pair[0]] = pair[1]
	}
	return labels, nil
}

// marshalMapInOrder marshalls a map into json in the order of the original
// data structure.
func marshalMapInOrder(m map[string]interface{}, t interface{}) (string, error) {
	s := "{"
	v := reflect.ValueOf(t)
	for i := 0; i < v.Type().NumField(); i++ {
		field := jsonFieldFromTag(v.Type().Field(i).Tag)
		if field == "" {
			continue
		}
		value, err := json.Marshal(m[field])
		if err != nil {
			return "", err
		}
		s += fmt.Sprintf("%q:%s,", field, value)
	}
	s = s[:len(s)-1]
	s += "}"
	var buf bytes.Buffer
	if err := json.Indent(&buf, []byte(s), "", "  "); err != nil {
		return "", err
	}
	return buf.String(), nil
}

// jsonFieldFromTag gets json field name from field tag.
func jsonFieldFromTag(tag reflect.StructTag) string {
	field := strings.Split(tag.Get("json"), ",")[0]
	for _, f := range strings.Split(tag.Get("protobuf"), ",") {
		if !strings.HasPrefix(f, "json=") {
			continue
		}
		field = strings.TrimPrefix(f, "json=")
	}
	return field
}

func getTruncatedID(id, prefix string) string {
	id = strings.TrimPrefix(id, prefix)
	if len(id) > truncatedIDLen {
		id = id[:truncatedIDLen]
	}
	return id
}
<commit_msg>crictl: Fallback if info key is not JSON<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"sort"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/urfave/cli"
	"google.golang.org/grpc"
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
	pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)

const (
	// truncatedImageIDLen is the truncated length of imageID
	truncatedIDLen = 13
)

var runtimeClient pb.RuntimeServiceClient
var imageClient pb.ImageServiceClient
var conn *grpc.ClientConn

type listOptions struct {
	// id of container or sandbox
	id string
	// podID of container
	podID string
	// Regular expression pattern to match pod name
	podNameRegexp string
	// Regular expression pattern to match the pod namespace
	podNamespaceRegexp string
	// state of the sandbox
	state string
	// show verbose info for the sandbox
	verbose bool
	// labels are selectors for the sandbox
	labels map[string]string
	// quiet is for listing just container/sandbox/image IDs
	quiet bool
	// output format
	output string
	// all containers
	all bool
	// latest container
	latest bool
	// last n containers
	last int
	// out with truncating the id
	noTrunc bool
}

type execOptions struct {
	// id of container
	id string
	// timeout to stop command
	timeout int64
	// Whether to exec a command in a tty
	tty bool
	// Whether to stream stdin
	stdin bool
	// Command to exec
	cmd []string
}
type attachOptions struct {
	// id of container
	id string
	// Whether the stdin is TTY
	tty bool
	// Whether pass Stdin to container
	stdin bool
}

type portforwardOptions struct {
	// id of sandbox
	id string
	// ports to forward
	ports []string
}

func getSortedKeys(m map[string]string) []string {
	var keys []string
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	return keys
}

func loadContainerConfig(path string) (*pb.ContainerConfig, error) {
	f, err := openFile(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var config pb.ContainerConfig
	if err := utilyaml.NewYAMLOrJSONDecoder(f, 4096).Decode(&config); err != nil {
		return nil, err
	}
	return &config, nil
}

func loadPodSandboxConfig(path string) (*pb.PodSandboxConfig, error) {
	f, err := openFile(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var config pb.PodSandboxConfig
	if err := utilyaml.NewYAMLOrJSONDecoder(f, 4096).Decode(&config); err != nil {
		return nil, err
	}
	return &config, nil
}

func openFile(path string) (*os.File, error) {
	f, err := os.Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, fmt.Errorf("config at %s not found", path)
		}
		return nil, err
	}
	return f, nil
}

func getRuntimeClient(context *cli.Context) error {
	// Set up a connection to the server.
	var err error
	conn, err = getRuntimeClientConnection(context)
	if err != nil {
		return fmt.Errorf("failed to connect: %v", err)
	}
	runtimeClient = pb.NewRuntimeServiceClient(conn)
	return nil
}

func getImageClient(context *cli.Context) error {
	// Set up a connection to the server.
	var err error
	conn, err = getImageClientConnection(context)
	if err != nil {
		return fmt.Errorf("failed to connect: %v", err)
	}
	imageClient = pb.NewImageServiceClient(conn)
	return nil
}

func closeConnection(context *cli.Context) error {
	if conn == nil {
		return nil
	}

	return conn.Close()
}

func protobufObjectToJSON(obj proto.Message) (string, error) {
	jsonpbMarshaler := jsonpb.Marshaler{EmitDefaults: true, Indent: "  "}
	marshaledJSON, err := jsonpbMarshaler.MarshalToString(obj)
	if err != nil {
		return "", err
	}
	return marshaledJSON, nil
}

func outputProtobufObjAsJSON(obj proto.Message) error {
	marshaledJSON, err := protobufObjectToJSON(obj)
	if err != nil {
		return err
	}

	fmt.Println(marshaledJSON)
	return nil
}

func outputProtobufObjAsYAML(obj proto.Message) error {
	marshaledJSON, err := protobufObjectToJSON(obj)
	if err != nil {
		return err
	}
	marshaledYAML, err := yaml.JSONToYAML([]byte(marshaledJSON))
	if err != nil {
		return err
	}

	fmt.Println(string(marshaledYAML))
	return nil
}

func outputStatusInfo(status string, info map[string]string, format string) error {
	// Sort all keys
	var keys []string
	for k := range info {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	jsonInfo := "{" + "\"status\":" + status + ","
	for _, k := range keys {
		var res interface{}
		// We attempt to convert key into JSON if possible else use it directly
		if err := json.Unmarshal([]byte(info[k]), res); err != nil {
			jsonInfo += "\"" + k + "\"" + ":" + "\"" + info[k] + "\","
		} else {
			jsonInfo += "\"" + k + "\"" + ":" + info[k] + ","
		}
	}
	jsonInfo = jsonInfo[:len(jsonInfo)-1]
	jsonInfo += "}"

	switch format {
	case "yaml":
		yamlInfo, err := yaml.JSONToYAML([]byte(jsonInfo))
		if err != nil {
			return err
		}
		fmt.Println(string(yamlInfo))
	case "json":
		var output bytes.Buffer
		if err := json.Indent(&output, []byte(jsonInfo), "", "  "); err != nil {
			return err
		}
		fmt.Println(output.String())
	default:
		fmt.Printf("Don't support %q format\n", format)
	}
	return nil
}

func parseLabelStringSlice(ss []string) (map[string]string, error) {
	labels := make(map[string]string)
	for _, s := range ss {
		pair := strings.Split(s, "=")
		if len(pair) != 2 {
			return nil, fmt.Errorf("incorrectly specified label: %v", s)
		}
		labels[pair[0]] = pair[1]
	}
	return labels, nil
}

// marshalMapInOrder marshalls a map into json in the order of the original
// data structure.
func marshalMapInOrder(m map[string]interface{}, t interface{}) (string, error) {
	s := "{"
	v := reflect.ValueOf(t)
	for i := 0; i < v.Type().NumField(); i++ {
		field := jsonFieldFromTag(v.Type().Field(i).Tag)
		if field == "" {
			continue
		}
		value, err := json.Marshal(m[field])
		if err != nil {
			return "", err
		}
		s += fmt.Sprintf("%q:%s,", field, value)
	}
	s = s[:len(s)-1]
	s += "}"
	var buf bytes.Buffer
	if err := json.Indent(&buf, []byte(s), "", "  "); err != nil {
		return "", err
	}
	return buf.String(), nil
}

// jsonFieldFromTag gets json field name from field tag.
func jsonFieldFromTag(tag reflect.StructTag) string {
	field := strings.Split(tag.Get("json"), ",")[0]
	for _, f := range strings.Split(tag.Get("protobuf"), ",") {
		if !strings.HasPrefix(f, "json=") {
			continue
		}
		field = strings.TrimPrefix(f, "json=")
	}
	return field
}

func getTruncatedID(id, prefix string) string {
	id = strings.TrimPrefix(id, prefix)
	if len(id) > truncatedIDLen {
		id = id[:truncatedIDLen]
	}
	return id
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

// Crypt is a simple password-based encryption program,
// demonstrating how to use code.google.com/p/rsc/crypt.
//
// Encrypt input to output using password:
//	crypt password <input >output
//
// Decrypt input to output using password:
//	crypt -d password <input >output
//
// Yes, the password is a command-line argument. This is a demo of the
// code.google.com/p/rsc/crypt package. It's not intended for real use.
//
package main

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"code.google.com/p/rsc/crypt"
)

func main() {
	args := os.Args[1:]
	encrypt := true
	if len(args) >= 1 && args[0] == "-d" {
		encrypt = false
		args = args[1:]
	}
	if len(args) != 1 || strings.HasPrefix(args[0], "-") {
		fmt.Fprintf(os.Stderr, "usage: crypt [-d] password < input > output\n")
		os.Exit(2)
	}
	password := args[0]

	data, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "reading stdin: %v\n", err)
		os.Exit(1)
	}
	if encrypt {
		pkt, err := crypt.Encrypt(password, data)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		str := base64.StdEncoding.EncodeToString(pkt)
		for len(str) > 60 {
			fmt.Printf("%s\n", str[:60])
			str = str[60:]
		}
		fmt.Printf("%s\n", str)
	} else {
		pkt, err := base64.StdEncoding.DecodeString(strings.Map(noSpace, string(data)))
		if err != nil {
			fmt.Fprintf(os.Stderr, "decoding input: %v\n", err)
			os.Exit(1)
		}
		dec, err := crypt.Decrypt(password, pkt)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		os.Stdout.Write(dec)
	}
}

func noSpace(r rune) rune {
	if r == ' ' || r == '\t' || r == '\n' {
		return -1
	}
	return r
}
<commit_msg>gofmt<commit_after>// Copyright 2012 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.

// Crypt is a simple password-based encryption program,
// demonstrating how to use code.google.com/p/rsc/crypt.
//
// Encrypt input to output using password:
//	crypt password <input >output
//
// Decrypt input to output using password:
//	crypt -d password <input >output
//
// Yes, the password is a command-line argument. This is a demo of the
// code.google.com/p/rsc/crypt package. It's not intended for real use.
//
package main

import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"code.google.com/p/rsc/crypt"
)

func main() {
	args := os.Args[1:]
	encrypt := true
	if len(args) >= 1 && args[0] == "-d" {
		encrypt = false
		args = args[1:]
	}
	if len(args) != 1 || strings.HasPrefix(args[0], "-") {
		fmt.Fprintf(os.Stderr, "usage: crypt [-d] password < input > output\n")
		os.Exit(2)
	}
	password := args[0]

	data, err := ioutil.ReadAll(os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "reading stdin: %v\n", err)
		os.Exit(1)
	}
	if encrypt {
		pkt, err := crypt.Encrypt(password, data)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		str := base64.StdEncoding.EncodeToString(pkt)
		for len(str) > 60 {
			fmt.Printf("%s\n", str[:60])
			str = str[60:]
		}
		fmt.Printf("%s\n", str)
	} else {
		pkt, err := base64.StdEncoding.DecodeString(strings.Map(noSpace, string(data)))
		if err != nil {
			fmt.Fprintf(os.Stderr, "decoding input: %v\n", err)
			os.Exit(1)
		}
		dec, err := crypt.Decrypt(password, pkt)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			os.Exit(1)
		}
		os.Stdout.Write(dec)
	}
}

func noSpace(r rune) rune {
	if r == ' ' || r == '\t' || r == '\n' {
		return -1
	}
	return r
}
<|endoftext|>


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

package main

import (
	"errors"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"code.google.com/p/go.tools/go/types"
)

var (
	source  = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers")
	verbose = flag.Bool("v", false, "verbose mode")
)

// lists of registered sources and corresponding importers
var (
	sources      []string
	importers    []types.Importer
	importFailed = errors.New("import failed")
)

// map of imported packages
var packages = make(map[string]*types.Package)

func usage() {
	fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
	flag.PrintDefaults()
	os.Exit(2)
}

func report(msg string) {
	fmt.Fprintln(os.Stderr, "error: "+msg)
	os.Exit(2)
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() == 0 {
		report("no package name, path, or file provided")
	}

	imp := tryImports
	if *source != "" {
		imp = lookup(*source)
		if imp == nil {
			report("source (-s argument) must be one of: " + strings.Join(sources, ", "))
		}
	}

	for _, arg := range flag.Args() {
		path, name := splitPathIdent(arg)
		logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name)

		// generate possible package path prefixes
		// (at the moment we do this for each argument - should probably cache the generated prefixes)
		prefixes := make(chan string)
		go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path))

		// import package
		pkg, err := tryPrefixes(packages, prefixes, path, imp)
		if err != nil {
			logf("\t=> ignoring %q: %s\n", path, err)
			continue
		}

		// filter objects if needed
		var filter func(types.Object) bool
		if name != "" {
			filter = func(obj types.Object) bool {
				// TODO(gri) perhaps use regular expression matching here?
				return obj.Name() == name
			}
		}

		// print contents
		print(os.Stdout, pkg, filter)
	}
}

func logf(format string, args ...interface{}) {
	if *verbose {
		fmt.Fprintf(os.Stderr, format, args...)
	}
}

// splitPathIdent splits a path.name argument into its components.
// All but the last path element may contain dots.
func splitPathIdent(arg string) (path, name string) {
	if i := strings.LastIndex(arg, "."); i >= 0 {
		if j := strings.LastIndex(arg, "/"); j < i {
			// '.' is not part of path
			path = arg[:i]
			name = arg[i+1:]
			return
		}
	}
	path = arg
	return
}

// tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp
// by prepending all possible prefixes to path. It returns with the first package that it could import, or
// with an error.
func tryPrefixes(packages map[string]*types.Package, prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) {
	for prefix := range prefixes {
		logf("\ttrying prefix %q\n", prefix)
		prepath := filepath.Join(prefix, path)
		pkg, err = imp(packages, prepath)
		if err == nil {
			break
		}
		logf("\t=> importing %q failed: %s\n", prepath, err)
	}
	return
}

// tryImports is an importer that tries all registered importers
// successively until one of them succeeds or all of them failed.
func tryImports(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
	for i, imp := range importers {
		logf("\t\ttrying %s import\n", sources[i])
		pkg, err = imp(packages, path)
		if err == nil {
			break
		}
		logf("\t\t=> %s import failed: %s\n", sources[i], err)
	}
	return
}

// protect protects an importer imp from panics and returns the protected importer.
func protect(imp types.Importer) types.Importer {
	return func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
		defer func() {
			if recover() != nil {
				pkg = nil
				err = importFailed
			}
		}()
		return imp(packages, path)
	}
}

// register registers an importer imp for a given source src.
func register(src string, imp types.Importer) {
	if lookup(src) != nil {
		panic(src + " importer already registered")
	}
	sources = append(sources, src)
	importers = append(importers, protect(imp))
}

// lookup returns the importer imp for a given source src.
func lookup(src string) types.Importer {
	for i, s := range sources {
		if s == src {
			return importers[i]
		}
	}
	return nil
}

func genPrefixes(out chan string, all bool) {
	out <- ""
	if all {
		platform := build.Default.GOOS + "_" + build.Default.GOARCH
		dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...)
		for _, dirname := range dirnames {
			walkDir(filepath.Join(dirname, "pkg", platform), "", out)
		}
	}
	close(out)
}

func walkDir(dirname, prefix string, out chan string) {
	fiList, err := ioutil.ReadDir(dirname)
	if err != nil {
		return
	}
	for _, fi := range fiList {
		if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
			prefix := filepath.Join(prefix, fi.Name())
			out <- prefix
			walkDir(filepath.Join(dirname, fi.Name()), prefix, out)
		}
	}
}
<commit_msg>go.tools/cmd/godex: make relative package paths work<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"errors"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"code.google.com/p/go.tools/go/types"
)

var (
	source  = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers")
	verbose = flag.Bool("v", false, "verbose mode")
)

// lists of registered sources and corresponding importers
var (
	sources      []string
	importers    []types.Importer
	importFailed = errors.New("import failed")
)

// map of imported packages
var packages = make(map[string]*types.Package)

func usage() {
	fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
	flag.PrintDefaults()
	os.Exit(2)
}

func report(msg string) {
	fmt.Fprintln(os.Stderr, "error: "+msg)
	os.Exit(2)
}

func main() {
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() == 0 {
		report("no package name, path, or file provided")
	}

	imp := tryImports
	if *source != "" {
		imp = lookup(*source)
		if imp == nil {
			report("source (-s argument) must be one of: " + strings.Join(sources, ", "))
		}
	}

	for _, arg := range flag.Args() {
		path, name := splitPathIdent(arg)
		logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name)

		// generate possible package path prefixes
		// (at the moment we do this for each argument - should probably cache the generated prefixes)
		prefixes := make(chan string)
		go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path))

		// import package
		pkg, err := tryPrefixes(packages, prefixes, path, imp)
		if err != nil {
			logf("\t=> ignoring %q: %s\n", path, err)
			continue
		}

		// filter objects if needed
		var filter func(types.Object) bool
		if name != "" {
			filter = func(obj types.Object) bool {
				// TODO(gri) perhaps use regular expression matching here?
				return obj.Name() == name
			}
		}

		// print contents
		print(os.Stdout, pkg, filter)
	}
}

func logf(format string, args ...interface{}) {
	if *verbose {
		fmt.Fprintf(os.Stderr, format, args...)
	}
}

// splitPathIdent splits a path.name argument into its components.
// All but the last path element may contain dots.
func splitPathIdent(arg string) (path, name string) {
	if i := strings.LastIndex(arg, "."); i >= 0 {
		if j := strings.LastIndex(arg, "/"); j < i {
			// '.' is not part of path
			path = arg[:i]
			name = arg[i+1:]
			return
		}
	}
	path = arg
	return
}

// tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp
// by prepending all possible prefixes to path. It returns with the first package that it could import, or
// with an error.
func tryPrefixes(packages map[string]*types.Package, prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) {
	for prefix := range prefixes {
		actual := path
		if prefix == "" {
			// don't use filepath.Join as it will sanitize the path and remove
			// a leading dot and then the path is not recognized as a relative
			// package path by the importers anymore
			logf("\ttrying no prefix\n")
		} else {
			actual = filepath.Join(prefix, path)
			logf("\ttrying prefix %q\n", prefix)
		}
		pkg, err = imp(packages, actual)
		if err == nil {
			break
		}
		logf("\t=> importing %q failed: %s\n", actual, err)
	}
	return
}

// tryImports is an importer that tries all registered importers
// successively until one of them succeeds or all of them failed.
func tryImports(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
	for i, imp := range importers {
		logf("\t\ttrying %s import\n", sources[i])
		pkg, err = imp(packages, path)
		if err == nil {
			break
		}
		logf("\t\t=> %s import failed: %s\n", sources[i], err)
	}
	return
}

// protect protects an importer imp from panics and returns the protected importer.
func protect(imp types.Importer) types.Importer {
	return func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
		defer func() {
			if recover() != nil {
				pkg = nil
				err = importFailed
			}
		}()
		return imp(packages, path)
	}
}

// register registers an importer imp for a given source src.
func register(src string, imp types.Importer) {
	if lookup(src) != nil {
		panic(src + " importer already registered")
	}
	sources = append(sources, src)
	importers = append(importers, protect(imp))
}

// lookup returns the importer imp for a given source src.
func lookup(src string) types.Importer {
	for i, s := range sources {
		if s == src {
			return importers[i]
		}
	}
	return nil
}

func genPrefixes(out chan string, all bool) {
	out <- ""
	if all {
		platform := build.Default.GOOS + "_" + build.Default.GOARCH
		dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...)
		for _, dirname := range dirnames {
			walkDir(filepath.Join(dirname, "pkg", platform), "", out)
		}
	}
	close(out)
}

func walkDir(dirname, prefix string, out chan string) {
	fiList, err := ioutil.ReadDir(dirname)
	if err != nil {
		return
	}
	for _, fi := range fiList {
		if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") {
			prefix := filepath.Join(prefix, fi.Name())
			out <- prefix
			walkDir(filepath.Join(dirname, fi.Name()), prefix, out)
		}
	}
}
<|endoftext|>


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

package main

import (
	"archive/tar"
	"bufio"
	"bytes"
	"compress/gzip"
	"context"
	"crypto/sha1"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"

	"golang.org/x/build/buildlet"
)

func push(args []string) error {
	fs := flag.NewFlagSet("push", flag.ContinueOnError)
	var dryRun bool
	fs.BoolVar(&dryRun, "dry-run", false, "print what would be done only")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, "push usage: gomote push <instance>")
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	goroot := os.Getenv("GOROOT")
	if goroot == "" {
		slurp, err := exec.Command("go", "env", "GOROOT").Output()
		if err != nil {
			return fmt.Errorf("failed to get GOROOT from go env: %v", err)
		}
		goroot = strings.TrimSpace(string(slurp))
		if goroot == "" {
			return errors.New("Failed to get $GOROOT from environment or go env")
		}
	}
	goroot = filepath.Clean(goroot)

	if fs.NArg() != 1 {
		fs.Usage()
	}
	name := fs.Arg(0)
	bc, conf, err := clientAndConf(name)
	if err != nil {
		return err
	}

	haveGo14 := false
	remote := map[string]buildlet.DirEntry{} // keys like "src/make.bash"

	lsOpts := buildlet.ListDirOpts{
		Recursive: true,
		Digest:    true,
		Skip: []string{
			// Ignore binary output directories:
			"go/pkg", "go/bin",
			// We don't care about the digest of
			// particular source files for Go 1.4.  And
			// exclude /pkg. This leaves go1.4/bin, which
			// is enough to know whether we have Go 1.4 or
			// not.
			"go1.4/src", "go1.4/pkg",
		},
	}
	ctx := context.Background()
	if err := bc.ListDir(ctx, ".", lsOpts, func(ent buildlet.DirEntry) {
		name := ent.Name()
		if strings.HasPrefix(name, "go1.4/") {
			haveGo14 = true
			return
		}
		if strings.HasPrefix(name, "go/") {
			remote[name[len("go/"):]] = ent
		}

	}); err != nil {
		return fmt.Errorf("error listing buildlet's existing files: %v", err)
	}

	if !haveGo14 {
		if u := conf.GoBootstrapURL(buildEnv); u != "" {
			log.Printf("installing go1.4")
			if dryRun {
				log.Printf("(Dry-run) Would have pushed go1.4")
			} else {
				if err := bc.PutTarFromURL(ctx, u, "go1.4"); err != nil {
					return err
				}
			}
		}
	}

	// Invoke 'git check-ignore' and use it to query whether paths have been gitignored.
	// If anything goes wrong at any point, fall back to assuming that nothing is gitignored.
	var isGitIgnored func(string) bool
	gci := exec.Command("git", "check-ignore", "--stdin", "-n", "-v", "-z")
	gci.Env = append(os.Environ(), "GIT_FLUSH=1")
	gciIn, errIn := gci.StdinPipe()
	defer gciIn.Close() // allow git process to exit
	gciOut, errOut := gci.StdoutPipe()
	errStart := gci.Start()
	if errIn != nil || errOut != nil || errStart != nil {
		isGitIgnored = func(string) bool { return false }
	} else {
		var failed bool
		br := bufio.NewReader(gciOut)
		isGitIgnored = func(path string) bool {
			if failed {
				return false
			}
			fmt.Fprintf(gciIn, "%s\x00", path)
			// Response is of form "<source> <NULL> <linenum> <NULL> <pattern> <NULL> <pathname> <NULL>"
			// Read all four and check that the path is correct.
			// If so, the path is ignored iff the source (reason why ignored) is non-empty.
			var resp [4][]byte
			for i := range resp {
				b, err := br.ReadBytes(0)
				if err != nil {
					failed = true
					return false
				}
				resp[i] = b[:len(b)-1] // drop trailing NULL
			}
			// Sanity check
			if string(resp[3]) != path {
				panic("git check-ignore path did not roundtrip, got " + string(resp[3]) + " sent " + path)
			}
			return len(resp[0]) > 0
		}
	}

	type fileInfo struct {
		fi   os.FileInfo
		sha1 string // if regular file
	}
	local := map[string]fileInfo{} // keys like "src/make.bash"
	if err := filepath.Walk(goroot, func(path string, fi os.FileInfo, err error) error {
		if isEditorBackup(path) {
			return nil
		}
		if err != nil {
			return err
		}
		rel, err := filepath.Rel(goroot, path)
		if err != nil {
			return fmt.Errorf("error calculating relative path from %q to %q", goroot, path)
		}
		rel = filepath.ToSlash(rel)
		if rel == "." {
			return nil
		}
		if fi.IsDir() {
			switch rel {
			case ".git", "pkg", "bin":
				return filepath.SkipDir
			}
		}
		inf := fileInfo{fi: fi}
		if isGitIgnored(path) {
			if fi.Mode().IsDir() {
				return filepath.SkipDir
			}
			return nil
		}
		if fi.Mode().IsRegular() {
			inf.sha1, err = fileSHA1(path)
			if err != nil {
				return err
			}
		}
		local[rel] = inf
		return nil
	}); err != nil {
		return fmt.Errorf("error enumerating local GOROOT files: %v", err)
	}

	var toDel []string
	for rel := range remote {
		if rel == "VERSION" {
			// Don't delete this. It's harmless, and
			// necessary. Clients can overwrite it if they
			// want. But if there's no VERSION file there,
			// make.bash/bat assumes there's a git repo in
			// place, but there's not only not a git repo
			// there with gomote, but there's no git tool
			// available either.
			continue
		}
		// Also don't delete the auto-generated files from cmd/dist.
		// Otherwise gomote users can't gomote push + gomote run make.bash
		// and then iteratively:
		// -- hack locally
		// -- gomote push
		// -- gomote run go test -v ...
		// Because the go test would fail remotely without
		// these files if they were deleted by gomote push.
		switch rel {
		case "src/cmd/cgo/zdefaultcc.go",
			"src/cmd/go/internal/cfg/zdefaultcc.go",
			"src/cmd/go/internal/cfg/zosarch.go",
			"src/cmd/internal/objabi/zbootstrap.go",
			"src/go/build/zcgo.go",
			"src/runtime/internal/sys/zversion.go":
			continue
		}
		if isGitIgnored(rel) {
			// Don't delete remote gitignored files; this breaks built toolchains.
			continue
		}
		rel = strings.TrimRight(rel, "/")
		if rel == "" {
			continue
		}
		if _, ok := local[rel]; !ok {
			toDel = append(toDel, rel)
		}
	}
	if len(toDel) > 0 {
		withGo := make([]string, len(toDel)) // with the "go/" prefix
		for i, v := range toDel {
			withGo[i] = "go/" + v
		}
		sort.Strings(withGo)
		if dryRun {
			log.Printf("(Dry-run) Would have deleted remote files: %q", withGo)
		} else {
			log.Printf("Deleting remote files: %q", withGo)
			if err := bc.RemoveAll(ctx, withGo...); err != nil {
				return fmt.Errorf("Deleting remote unwanted files: %v", err)
			}
		}
	}

	var toSend []string
	notHave := 0
	const maxNotHavePrint = 5
	for rel, inf := range local {
		switch rel {
		case "VERSION.cache", "src/runtime/internal/sys/zversion.go", "src/cmd/internal/objabi/zbootstrap.go",
			"src/go/build/zcgo.go":
			continue
		}
		if !inf.fi.Mode().IsRegular() {
			// TODO(bradfitz): this is only doing regular files
			// for now, so empty directories, symlinks, etc aren't
			// supported. revisit if that's a problem.
			if !inf.fi.IsDir() {
				log.Printf("Ignoring local non-regular, non-directory file %s: %v", rel, inf.fi.Mode())
			}
			continue
		}
		rem, ok := remote[rel]
		if !ok {
			if notHave++; notHave <= maxNotHavePrint {
				log.Printf("Remote doesn't have %q", rel)
			}
			toSend = append(toSend, rel)
			continue
		}
		if rem.Digest() != inf.sha1 {
			log.Printf("Remote's %s digest is %q; want %q", rel, rem.Digest(), inf.sha1)
			toSend = append(toSend, rel)
		}
	}
	if notHave > maxNotHavePrint {
		log.Printf("Remote doesn't have %d files (only showed %d).", notHave, maxNotHavePrint)
	}
	if _, hasVersion := remote["VERSION"]; !hasVersion {
		log.Printf("Remote lacks a VERSION file; sending a fake one")
		toSend = append(toSend, "VERSION")
	}
	if len(toSend) > 0 {
		sort.Strings(toSend)
		tgz, err := generateDeltaTgz(goroot, toSend)
		if err != nil {
			return err
		}
		log.Printf("Uploading %d new/changed files; %d byte .tar.gz", len(toSend), tgz.Len())
		if dryRun {
			log.Printf("(Dry-run mode; not doing anything.")
			return nil
		}
		if err := bc.PutTar(ctx, tgz, "go"); err != nil {
			return fmt.Errorf("writing tarball to buildlet: %v", err)
		}
	}
	return nil
}

func isEditorBackup(path string) bool {
	base := filepath.Base(path)
	if strings.HasPrefix(base, ".") && strings.HasSuffix(base, ".swp") {
		// vi
		return true
	}
	if strings.HasSuffix(path, "~") || strings.HasSuffix(path, "#") ||
		strings.HasPrefix(base, "#") || strings.HasPrefix(base, ".#") {
		// emacs
		return true
	}
	return false
}

// file is forward-slash separated
func generateDeltaTgz(goroot string, files []string) (*bytes.Buffer, error) {
	var buf bytes.Buffer
	zw := gzip.NewWriter(&buf)
	tw := tar.NewWriter(zw)
	for _, file := range files {
		// Special.
		if file == "VERSION" {
			// TODO(bradfitz): a dummy VERSION file's contents to make things
			// happy. Notably it starts with "devel ". Do we care about it
			// being accurate beyond that?
			version := "devel gomote.XXXXX"
			if err := tw.WriteHeader(&tar.Header{
				Name: "VERSION",
				Mode: 0644,
				Size: int64(len(version)),
			}); err != nil {
				return nil, err
			}
			if _, err := io.WriteString(tw, version); err != nil {
				return nil, err
			}
			continue
		}
		f, err := os.Open(filepath.Join(goroot, file))
		if err != nil {
			return nil, err
		}
		fi, err := f.Stat()
		if err != nil {
			f.Close()
			return nil, err
		}
		header, err := tar.FileInfoHeader(fi, "")
		if err != nil {
			f.Close()
			return nil, err
		}
		header.Name = file // forward slash
		if err := tw.WriteHeader(header); err != nil {
			f.Close()
			return nil, err
		}
		if _, err := io.CopyN(tw, f, header.Size); err != nil {
			f.Close()
			return nil, fmt.Errorf("error copying contents of %s: %v", file, err)
		}
		f.Close()
	}
	if err := tw.Close(); err != nil {
		return nil, err
	}
	if err := zw.Close(); err != nil {
		return nil, err
	}

	return &buf, nil
}

func fileSHA1(path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()
	s1 := sha1.New()
	if _, err := io.Copy(s1, f); err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", s1.Sum(nil)), nil
}
<commit_msg>cmd/gomote: fix inconsistencies in push exclusion list handling<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"archive/tar"
	"bufio"
	"bytes"
	"compress/gzip"
	"context"
	"crypto/sha1"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"

	"golang.org/x/build/buildlet"
)

func push(args []string) error {
	fs := flag.NewFlagSet("push", flag.ContinueOnError)
	var dryRun bool
	fs.BoolVar(&dryRun, "dry-run", false, "print what would be done only")
	fs.Usage = func() {
		fmt.Fprintln(os.Stderr, "push usage: gomote push <instance>")
		fs.PrintDefaults()
		os.Exit(1)
	}
	fs.Parse(args)

	goroot := os.Getenv("GOROOT")
	if goroot == "" {
		slurp, err := exec.Command("go", "env", "GOROOT").Output()
		if err != nil {
			return fmt.Errorf("failed to get GOROOT from go env: %v", err)
		}
		goroot = strings.TrimSpace(string(slurp))
		if goroot == "" {
			return errors.New("Failed to get $GOROOT from environment or go env")
		}
	}
	goroot = filepath.Clean(goroot)

	if fs.NArg() != 1 {
		fs.Usage()
	}
	name := fs.Arg(0)
	bc, conf, err := clientAndConf(name)
	if err != nil {
		return err
	}

	haveGo14 := false
	remote := map[string]buildlet.DirEntry{} // keys like "src/make.bash"

	lsOpts := buildlet.ListDirOpts{
		Recursive: true,
		Digest:    true,
		Skip: []string{
			// Ignore binary output directories:
			"go/pkg", "go/bin",
			// We don't care about the digest of
			// particular source files for Go 1.4.  And
			// exclude /pkg. This leaves go1.4/bin, which
			// is enough to know whether we have Go 1.4 or
			// not.
			"go1.4/src", "go1.4/pkg",
		},
	}
	ctx := context.Background()
	if err := bc.ListDir(ctx, ".", lsOpts, func(ent buildlet.DirEntry) {
		name := ent.Name()
		if strings.HasPrefix(name, "go1.4/") {
			haveGo14 = true
			return
		}
		if strings.HasPrefix(name, "go/") {
			remote[name[len("go/"):]] = ent
		}

	}); err != nil {
		return fmt.Errorf("error listing buildlet's existing files: %v", err)
	}

	if !haveGo14 {
		if u := conf.GoBootstrapURL(buildEnv); u != "" {
			log.Printf("installing go1.4")
			if dryRun {
				log.Printf("(Dry-run) Would have pushed go1.4")
			} else {
				if err := bc.PutTarFromURL(ctx, u, "go1.4"); err != nil {
					return err
				}
			}
		}
	}

	// Invoke 'git check-ignore' and use it to query whether paths have been gitignored.
	// If anything goes wrong at any point, fall back to assuming that nothing is gitignored.
	var isGitIgnored func(string) bool
	gci := exec.Command("git", "check-ignore", "--stdin", "-n", "-v", "-z")
	gci.Env = append(os.Environ(), "GIT_FLUSH=1")
	gciIn, errIn := gci.StdinPipe()
	defer gciIn.Close() // allow git process to exit
	gciOut, errOut := gci.StdoutPipe()
	errStart := gci.Start()
	if errIn != nil || errOut != nil || errStart != nil {
		isGitIgnored = func(string) bool { return false }
	} else {
		var failed bool
		br := bufio.NewReader(gciOut)
		isGitIgnored = func(path string) bool {
			if failed {
				return false
			}
			fmt.Fprintf(gciIn, "%s\x00", path)
			// Response is of form "<source> <NULL> <linenum> <NULL> <pattern> <NULL> <pathname> <NULL>"
			// Read all four and check that the path is correct.
			// If so, the path is ignored iff the source (reason why ignored) is non-empty.
			var resp [4][]byte
			for i := range resp {
				b, err := br.ReadBytes(0)
				if err != nil {
					failed = true
					return false
				}
				resp[i] = b[:len(b)-1] // drop trailing NULL
			}
			// Sanity check
			if string(resp[3]) != path {
				panic("git check-ignore path did not roundtrip, got " + string(resp[3]) + " sent " + path)
			}
			return len(resp[0]) > 0
		}
	}

	type fileInfo struct {
		fi   os.FileInfo
		sha1 string // if regular file
	}
	local := map[string]fileInfo{} // keys like "src/make.bash"
	if err := filepath.Walk(goroot, func(path string, fi os.FileInfo, err error) error {
		if isEditorBackup(path) {
			return nil
		}
		if err != nil {
			return err
		}
		rel, err := filepath.Rel(goroot, path)
		if err != nil {
			return fmt.Errorf("error calculating relative path from %q to %q", goroot, path)
		}
		rel = filepath.ToSlash(rel)
		if rel == "." {
			return nil
		}
		if fi.IsDir() {
			switch rel {
			case ".git", "pkg", "bin":
				return filepath.SkipDir
			}
		}
		inf := fileInfo{fi: fi}
		if isGitIgnored(path) {
			if fi.Mode().IsDir() {
				return filepath.SkipDir
			}
			return nil
		}
		if fi.Mode().IsRegular() {
			inf.sha1, err = fileSHA1(path)
			if err != nil {
				return err
			}
		}
		local[rel] = inf
		return nil
	}); err != nil {
		return fmt.Errorf("error enumerating local GOROOT files: %v", err)
	}

	var toDel []string
	for rel := range remote {
		if rel == "VERSION" {
			// Don't delete this. It's harmless, and
			// necessary. Clients can overwrite it if they
			// want. But if there's no VERSION file there,
			// make.bash/bat assumes there's a git repo in
			// place, but there's not only not a git repo
			// there with gomote, but there's no git tool
			// available either.
			continue
		}
		// Also don't delete the auto-generated files from cmd/dist.
		// Otherwise gomote users can't gomote push + gomote run make.bash
		// and then iteratively:
		// -- hack locally
		// -- gomote push
		// -- gomote run go test -v ...
		// Because the go test would fail remotely without
		// these files if they were deleted by gomote push.
		if isGoToolDistGenerated(rel) {
			continue
		}
		if isGitIgnored(rel) {
			// Don't delete remote gitignored files; this breaks built toolchains.
			continue
		}
		rel = strings.TrimRight(rel, "/")
		if rel == "" {
			continue
		}
		if _, ok := local[rel]; !ok {
			toDel = append(toDel, rel)
		}
	}
	if len(toDel) > 0 {
		withGo := make([]string, len(toDel)) // with the "go/" prefix
		for i, v := range toDel {
			withGo[i] = "go/" + v
		}
		sort.Strings(withGo)
		if dryRun {
			log.Printf("(Dry-run) Would have deleted remote files: %q", withGo)
		} else {
			log.Printf("Deleting remote files: %q", withGo)
			if err := bc.RemoveAll(ctx, withGo...); err != nil {
				return fmt.Errorf("Deleting remote unwanted files: %v", err)
			}
		}
	}

	var toSend []string
	notHave := 0
	const maxNotHavePrint = 5
	for rel, inf := range local {
		if isGoToolDistGenerated(rel) || rel == "VERSION.cache" {
			continue
		}
		if !inf.fi.Mode().IsRegular() {
			// TODO(bradfitz): this is only doing regular files
			// for now, so empty directories, symlinks, etc aren't
			// supported. revisit if that's a problem.
			if !inf.fi.IsDir() {
				log.Printf("Ignoring local non-regular, non-directory file %s: %v", rel, inf.fi.Mode())
			}
			continue
		}
		rem, ok := remote[rel]
		if !ok {
			if notHave++; notHave <= maxNotHavePrint {
				log.Printf("Remote doesn't have %q", rel)
			}
			toSend = append(toSend, rel)
			continue
		}
		if rem.Digest() != inf.sha1 {
			log.Printf("Remote's %s digest is %q; want %q", rel, rem.Digest(), inf.sha1)
			toSend = append(toSend, rel)
		}
	}
	if notHave > maxNotHavePrint {
		log.Printf("Remote doesn't have %d files (only showed %d).", notHave, maxNotHavePrint)
	}
	if _, hasVersion := remote["VERSION"]; !hasVersion {
		log.Printf("Remote lacks a VERSION file; sending a fake one")
		toSend = append(toSend, "VERSION")
	}
	if len(toSend) > 0 {
		sort.Strings(toSend)
		tgz, err := generateDeltaTgz(goroot, toSend)
		if err != nil {
			return err
		}
		log.Printf("Uploading %d new/changed files; %d byte .tar.gz", len(toSend), tgz.Len())
		if dryRun {
			log.Printf("(Dry-run mode; not doing anything.")
			return nil
		}
		if err := bc.PutTar(ctx, tgz, "go"); err != nil {
			return fmt.Errorf("writing tarball to buildlet: %v", err)
		}
	}
	return nil
}

func isGoToolDistGenerated(path string) bool {
	switch path {
	case "src/cmd/cgo/zdefaultcc.go",
		"src/cmd/go/internal/cfg/zdefaultcc.go",
		"src/cmd/go/internal/cfg/zosarch.go",
		"src/cmd/internal/objabi/zbootstrap.go",
		"src/go/build/zcgo.go",
		"src/internal/buildcfg/zbootstrap.go",
		"src/runtime/internal/sys/zversion.go":
		return true
	}
	return false
}

func isEditorBackup(path string) bool {
	base := filepath.Base(path)
	if strings.HasPrefix(base, ".") && strings.HasSuffix(base, ".swp") {
		// vi
		return true
	}
	if strings.HasSuffix(path, "~") || strings.HasSuffix(path, "#") ||
		strings.HasPrefix(base, "#") || strings.HasPrefix(base, ".#") {
		// emacs
		return true
	}
	return false
}

// file is forward-slash separated
func generateDeltaTgz(goroot string, files []string) (*bytes.Buffer, error) {
	var buf bytes.Buffer
	zw := gzip.NewWriter(&buf)
	tw := tar.NewWriter(zw)
	for _, file := range files {
		// Special.
		if file == "VERSION" {
			// TODO(bradfitz): a dummy VERSION file's contents to make things
			// happy. Notably it starts with "devel ". Do we care about it
			// being accurate beyond that?
			version := "devel gomote.XXXXX"
			if err := tw.WriteHeader(&tar.Header{
				Name: "VERSION",
				Mode: 0644,
				Size: int64(len(version)),
			}); err != nil {
				return nil, err
			}
			if _, err := io.WriteString(tw, version); err != nil {
				return nil, err
			}
			continue
		}
		f, err := os.Open(filepath.Join(goroot, file))
		if err != nil {
			return nil, err
		}
		fi, err := f.Stat()
		if err != nil {
			f.Close()
			return nil, err
		}
		header, err := tar.FileInfoHeader(fi, "")
		if err != nil {
			f.Close()
			return nil, err
		}
		header.Name = file // forward slash
		if err := tw.WriteHeader(header); err != nil {
			f.Close()
			return nil, err
		}
		if _, err := io.CopyN(tw, f, header.Size); err != nil {
			f.Close()
			return nil, fmt.Errorf("error copying contents of %s: %v", file, err)
		}
		f.Close()
	}
	if err := tw.Close(); err != nil {
		return nil, err
	}
	if err := zw.Close(); err != nil {
		return nil, err
	}

	return &buf, nil
}

func fileSHA1(path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		return "", err
	}
	defer f.Close()
	s1 := sha1.New()
	if _, err := io.Copy(s1, f); err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", s1.Sum(nil)), nil
}
<|endoftext|>