<commit_before>/*
 * MinIO Client (C) 2015 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 (
	"os"
	"path/filepath"
	"strings"

	"github.com/minio/mc/pkg/probe"
)

type copyURLsType uint8

//   NOTE: All the parse rules should reduced to A: Copy(Source, Target).
//
//   * VALID RULES
//   =======================
//   A: copy(f, f) -> copy(f, f)
//   B: copy(f, d) -> copy(f, d/f) -> []A
//   C: copy(d1..., d2) -> []copy(f, d2/d1/f) -> []A
//   D: copy([]f, d) -> []B

//   * INVALID RULES
//   =========================
//   copy(d, f)
//   copy(d..., f)
//   copy([](f|d)..., f)

const (
	copyURLsTypeInvalid copyURLsType = iota
	copyURLsTypeA
	copyURLsTypeB
	copyURLsTypeC
	copyURLsTypeD
)

// guessCopyURLType guesses the type of clientURL. This approach all allows prepareURL
// functions to accurately report failure causes.
func guessCopyURLType(sourceURLs []string, targetURL string, isRecursive bool, keys map[string][]prefixSSEPair) (copyURLsType, *probe.Error) {
	if len(sourceURLs) == 1 { // 1 Source, 1 Target
		sourceURL := sourceURLs[0]
		_, sourceContent, err := url2Stat(sourceURL, false, keys)
		if err != nil {
			return copyURLsTypeInvalid, err
		}

		// If recursion is ON, it is type C.
		// If source is a folder, it is Type C.
		if sourceContent.Type.IsDir() || isRecursive {
			return copyURLsTypeC, nil
		}

		// If target is a folder, it is Type B.
		if isAliasURLDir(targetURL, keys) {
			return copyURLsTypeB, nil
		}
		// else Type A.
		return copyURLsTypeA, nil
	}

	// Multiple source args and target is a folder. It is Type D.
	if isAliasURLDir(targetURL, keys) {
		return copyURLsTypeD, nil
	}

	return copyURLsTypeInvalid, errInvalidArgument().Trace()
}

// SINGLE SOURCE - Type A: copy(f, f) -> copy(f, f)
// prepareCopyURLsTypeA - prepares target and source clientURLs for copying.
func prepareCopyURLsTypeA(sourceURL string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	// Extract alias before fiddling with the clientURL.
	sourceAlias, _, _ := mustExpandAlias(sourceURL)
	// Find alias and expanded clientURL.
	targetAlias, targetURL, _ := mustExpandAlias(targetURL)

	_, sourceContent, err := url2Stat(sourceURL, false, encKeyDB)
	if err != nil {
		// Source does not exist or insufficient privileges.
		return URLs{Error: err.Trace(sourceURL)}
	}
	if !sourceContent.Type.IsRegular() {
		// Source is not a regular file
		return URLs{Error: errInvalidSource(sourceURL).Trace(sourceURL)}
	}

	// All OK.. We can proceed. Type A
	return makeCopyContentTypeA(sourceAlias, sourceContent, targetAlias, targetURL, encKeyDB)
}

// prepareCopyContentTypeA - makes CopyURLs content for copying.
func makeCopyContentTypeA(sourceAlias string, sourceContent *ClientContent, targetAlias string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	targetContent := ClientContent{URL: *newClientURL(targetURL)}
	return URLs{
		SourceAlias:   sourceAlias,
		SourceContent: sourceContent,
		TargetAlias:   targetAlias,
		TargetContent: &targetContent,
	}
}

// SINGLE SOURCE - Type B: copy(f, d) -> copy(f, d/f) -> A
// prepareCopyURLsTypeB - prepares target and source clientURLs for copying.
func prepareCopyURLsTypeB(sourceURL string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	// Extract alias before fiddling with the clientURL.
	sourceAlias, _, _ := mustExpandAlias(sourceURL)
	// Find alias and expanded clientURL.
	targetAlias, targetURL, _ := mustExpandAlias(targetURL)

	_, sourceContent, err := url2Stat(sourceURL, false, encKeyDB)
	if err != nil {
		// Source does not exist or insufficient privileges.
		return URLs{Error: err.Trace(sourceURL)}
	}

	if !sourceContent.Type.IsRegular() {
		if sourceContent.Type.IsDir() {
			return URLs{Error: errSourceIsDir(sourceURL).Trace(sourceURL)}
		}
		// Source is not a regular file.
		return URLs{Error: errInvalidSource(sourceURL).Trace(sourceURL)}
	}

	// All OK.. We can proceed. Type B: source is a file, target is a folder and exists.
	return makeCopyContentTypeB(sourceAlias, sourceContent, targetAlias, targetURL, encKeyDB)
}

// makeCopyContentTypeB - CopyURLs content for copying.
func makeCopyContentTypeB(sourceAlias string, sourceContent *ClientContent, targetAlias string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	// All OK.. We can proceed. Type B: source is a file, target is a folder and exists.
	targetURLParse := newClientURL(targetURL)
	targetURLParse.Path = filepath.ToSlash(filepath.Join(targetURLParse.Path, filepath.Base(sourceContent.URL.Path)))
	return makeCopyContentTypeA(sourceAlias, sourceContent, targetAlias, targetURLParse.String(), encKeyDB)
}

// SINGLE SOURCE - Type C: copy(d1..., d2) -> []copy(d1/f, d1/d2/f) -> []A
// prepareCopyRecursiveURLTypeC - prepares target and source clientURLs for copying.
func prepareCopyURLsTypeC(sourceURL, targetURL string, isRecursive bool, encKeyDB map[string][]prefixSSEPair) <-chan URLs {
	// Extract alias before fiddling with the clientURL.
	sourceAlias, _, _ := mustExpandAlias(sourceURL)
	// Find alias and expanded clientURL.
	targetAlias, targetURL, _ := mustExpandAlias(targetURL)
	copyURLsCh := make(chan URLs)
	go func(sourceURL, targetURL string, copyURLsCh chan URLs) {
		defer close(copyURLsCh)
		sourceClient, err := newClient(sourceURL)
		if err != nil {
			// Source initialization failed.
			copyURLsCh <- URLs{Error: err.Trace(sourceURL)}
			return
		}

		isIncomplete := false
		for sourceContent := range sourceClient.List(isRecursive, isIncomplete, false, DirNone) {
			if sourceContent.Err != nil {
				// Listing failed.
				copyURLsCh <- URLs{Error: sourceContent.Err.Trace(sourceClient.GetURL().String())}
				continue
			}

			if !sourceContent.Type.IsRegular() {
				// Source is not a regular file. Skip it for copy.
				continue
			}

			// All OK.. We can proceed. Type B: source is a file, target is a folder and exists.
			copyURLsCh <- makeCopyContentTypeC(sourceAlias, sourceClient.GetURL(), sourceContent, targetAlias, targetURL, encKeyDB)
		}
	}(sourceURL, targetURL, copyURLsCh)
	return copyURLsCh
}

// makeCopyContentTypeC - CopyURLs content for copying.
func makeCopyContentTypeC(sourceAlias string, sourceURL ClientURL, sourceContent *ClientContent, targetAlias string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	newSourceURL := sourceContent.URL
	pathSeparatorIndex := strings.LastIndex(sourceURL.Path, string(sourceURL.Separator))
	newSourceSuffix := filepath.ToSlash(newSourceURL.Path)
	if pathSeparatorIndex > 1 {
		sourcePrefix := filepath.ToSlash(sourceURL.Path[:pathSeparatorIndex])
		// do not preserve unix cp behavior when copying a filesytem dir to
		// objectstore.
		if sourceAlias == "" && targetAlias != "" {
			// Check if sourceURL.Path is a directory or not
			fileInfo, err := os.Stat(sourceURL.Path)
			if err != nil {
				return URLs{Error: probe.NewError(err)}
			}
			if fileInfo.IsDir() {
				sourcePrefix = sourceURL.Path
			}
		}
		newSourceSuffix = strings.TrimPrefix(newSourceSuffix, sourcePrefix)
	}
	newTargetURL := urlJoinPath(targetURL, newSourceSuffix)
	return makeCopyContentTypeA(sourceAlias, sourceContent, targetAlias, newTargetURL, encKeyDB)
}

// MULTI-SOURCE - Type D: copy([](f|d...), d) -> []B
// prepareCopyURLsTypeE - prepares target and source clientURLs for copying.
func prepareCopyURLsTypeD(sourceURLs []string, targetURL string, isRecursive bool, encKeyDB map[string][]prefixSSEPair) <-chan URLs {
	copyURLsCh := make(chan URLs)
	go func(sourceURLs []string, targetURL string, copyURLsCh chan URLs) {
		defer close(copyURLsCh)
		for _, sourceURL := range sourceURLs {
			for cpURLs := range prepareCopyURLsTypeC(sourceURL, targetURL, isRecursive, encKeyDB) {
				copyURLsCh <- cpURLs
			}
		}
	}(sourceURLs, targetURL, copyURLsCh)
	return copyURLsCh
}

// prepareCopyURLs - prepares target and source clientURLs for copying.
func prepareCopyURLs(sourceURLs []string, targetURL string, isRecursive bool, encKeyDB map[string][]prefixSSEPair, olderThan, newerThan string) chan URLs {
	copyURLsCh := make(chan URLs)
	go func(sourceURLs []string, targetURL string, copyURLsCh chan URLs, encKeyDB map[string][]prefixSSEPair) {
		defer close(copyURLsCh)
		cpType, err := guessCopyURLType(sourceURLs, targetURL, isRecursive, encKeyDB)
		fatalIf(err.Trace(), "Unable to guess the type of copy operation.")

		switch cpType {
		case copyURLsTypeA:
			copyURLsCh <- prepareCopyURLsTypeA(sourceURLs[0], targetURL, encKeyDB)
		case copyURLsTypeB:
			copyURLsCh <- prepareCopyURLsTypeB(sourceURLs[0], targetURL, encKeyDB)
		case copyURLsTypeC:
			for cURLs := range prepareCopyURLsTypeC(sourceURLs[0], targetURL, isRecursive, encKeyDB) {
				copyURLsCh <- cURLs
			}
		case copyURLsTypeD:
			for cURLs := range prepareCopyURLsTypeD(sourceURLs, targetURL, isRecursive, encKeyDB) {
				copyURLsCh <- cURLs
			}
		default:
			copyURLsCh <- URLs{Error: errInvalidArgument().Trace(sourceURLs...)}
		}
	}(sourceURLs, targetURL, copyURLsCh, encKeyDB)

	finalCopyURLsCh := make(chan URLs)
	go func() {
		defer close(finalCopyURLsCh)
		for cpURLs := range copyURLsCh {
			// Skip objects older than --older-than parameter if specified
			if olderThan != "" && isOlder(cpURLs.SourceContent.Time, olderThan) {
				continue
			}

			// Skip objects newer than --newer-than parameter if specified
			if newerThan != "" && isNewer(cpURLs.SourceContent.Time, newerThan) {
				continue
			}

			finalCopyURLsCh <- cpURLs
		}
	}()

	return finalCopyURLsCh
}
<commit_msg>cp: Fix Windows paths (#3250)<commit_after>/*
 * MinIO Client (C) 2015 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 (
	"os"
	"path/filepath"
	"strings"

	"github.com/minio/mc/pkg/probe"
)

type copyURLsType uint8

//   NOTE: All the parse rules should reduced to A: Copy(Source, Target).
//
//   * VALID RULES
//   =======================
//   A: copy(f, f) -> copy(f, f)
//   B: copy(f, d) -> copy(f, d/f) -> []A
//   C: copy(d1..., d2) -> []copy(f, d2/d1/f) -> []A
//   D: copy([]f, d) -> []B

//   * INVALID RULES
//   =========================
//   copy(d, f)
//   copy(d..., f)
//   copy([](f|d)..., f)

const (
	copyURLsTypeInvalid copyURLsType = iota
	copyURLsTypeA
	copyURLsTypeB
	copyURLsTypeC
	copyURLsTypeD
)

// guessCopyURLType guesses the type of clientURL. This approach all allows prepareURL
// functions to accurately report failure causes.
func guessCopyURLType(sourceURLs []string, targetURL string, isRecursive bool, keys map[string][]prefixSSEPair) (copyURLsType, *probe.Error) {
	if len(sourceURLs) == 1 { // 1 Source, 1 Target
		sourceURL := sourceURLs[0]
		_, sourceContent, err := url2Stat(sourceURL, false, keys)
		if err != nil {
			return copyURLsTypeInvalid, err
		}

		// If recursion is ON, it is type C.
		// If source is a folder, it is Type C.
		if sourceContent.Type.IsDir() || isRecursive {
			return copyURLsTypeC, nil
		}

		// If target is a folder, it is Type B.
		if isAliasURLDir(targetURL, keys) {
			return copyURLsTypeB, nil
		}
		// else Type A.
		return copyURLsTypeA, nil
	}

	// Multiple source args and target is a folder. It is Type D.
	if isAliasURLDir(targetURL, keys) {
		return copyURLsTypeD, nil
	}

	return copyURLsTypeInvalid, errInvalidArgument().Trace()
}

// SINGLE SOURCE - Type A: copy(f, f) -> copy(f, f)
// prepareCopyURLsTypeA - prepares target and source clientURLs for copying.
func prepareCopyURLsTypeA(sourceURL string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	// Extract alias before fiddling with the clientURL.
	sourceAlias, _, _ := mustExpandAlias(sourceURL)
	// Find alias and expanded clientURL.
	targetAlias, targetURL, _ := mustExpandAlias(targetURL)

	_, sourceContent, err := url2Stat(sourceURL, false, encKeyDB)
	if err != nil {
		// Source does not exist or insufficient privileges.
		return URLs{Error: err.Trace(sourceURL)}
	}
	if !sourceContent.Type.IsRegular() {
		// Source is not a regular file
		return URLs{Error: errInvalidSource(sourceURL).Trace(sourceURL)}
	}

	// All OK.. We can proceed. Type A
	return makeCopyContentTypeA(sourceAlias, sourceContent, targetAlias, targetURL, encKeyDB)
}

// prepareCopyContentTypeA - makes CopyURLs content for copying.
func makeCopyContentTypeA(sourceAlias string, sourceContent *ClientContent, targetAlias string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	targetContent := ClientContent{URL: *newClientURL(targetURL)}
	return URLs{
		SourceAlias:   sourceAlias,
		SourceContent: sourceContent,
		TargetAlias:   targetAlias,
		TargetContent: &targetContent,
	}
}

// SINGLE SOURCE - Type B: copy(f, d) -> copy(f, d/f) -> A
// prepareCopyURLsTypeB - prepares target and source clientURLs for copying.
func prepareCopyURLsTypeB(sourceURL string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	// Extract alias before fiddling with the clientURL.
	sourceAlias, _, _ := mustExpandAlias(sourceURL)
	// Find alias and expanded clientURL.
	targetAlias, targetURL, _ := mustExpandAlias(targetURL)

	_, sourceContent, err := url2Stat(sourceURL, false, encKeyDB)
	if err != nil {
		// Source does not exist or insufficient privileges.
		return URLs{Error: err.Trace(sourceURL)}
	}

	if !sourceContent.Type.IsRegular() {
		if sourceContent.Type.IsDir() {
			return URLs{Error: errSourceIsDir(sourceURL).Trace(sourceURL)}
		}
		// Source is not a regular file.
		return URLs{Error: errInvalidSource(sourceURL).Trace(sourceURL)}
	}

	// All OK.. We can proceed. Type B: source is a file, target is a folder and exists.
	return makeCopyContentTypeB(sourceAlias, sourceContent, targetAlias, targetURL, encKeyDB)
}

// makeCopyContentTypeB - CopyURLs content for copying.
func makeCopyContentTypeB(sourceAlias string, sourceContent *ClientContent, targetAlias string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	// All OK.. We can proceed. Type B: source is a file, target is a folder and exists.
	targetURLParse := newClientURL(targetURL)
	targetURLParse.Path = filepath.ToSlash(filepath.Join(targetURLParse.Path, filepath.Base(sourceContent.URL.Path)))
	return makeCopyContentTypeA(sourceAlias, sourceContent, targetAlias, targetURLParse.String(), encKeyDB)
}

// SINGLE SOURCE - Type C: copy(d1..., d2) -> []copy(d1/f, d1/d2/f) -> []A
// prepareCopyRecursiveURLTypeC - prepares target and source clientURLs for copying.
func prepareCopyURLsTypeC(sourceURL, targetURL string, isRecursive bool, encKeyDB map[string][]prefixSSEPair) <-chan URLs {
	// Extract alias before fiddling with the clientURL.
	sourceAlias, _, _ := mustExpandAlias(sourceURL)
	// Find alias and expanded clientURL.
	targetAlias, targetURL, _ := mustExpandAlias(targetURL)
	copyURLsCh := make(chan URLs)
	go func(sourceURL, targetURL string, copyURLsCh chan URLs) {
		defer close(copyURLsCh)
		sourceClient, err := newClient(sourceURL)
		if err != nil {
			// Source initialization failed.
			copyURLsCh <- URLs{Error: err.Trace(sourceURL)}
			return
		}

		isIncomplete := false
		for sourceContent := range sourceClient.List(isRecursive, isIncomplete, false, DirNone) {
			if sourceContent.Err != nil {
				// Listing failed.
				copyURLsCh <- URLs{Error: sourceContent.Err.Trace(sourceClient.GetURL().String())}
				continue
			}

			if !sourceContent.Type.IsRegular() {
				// Source is not a regular file. Skip it for copy.
				continue
			}

			// All OK.. We can proceed. Type B: source is a file, target is a folder and exists.
			copyURLsCh <- makeCopyContentTypeC(sourceAlias, sourceClient.GetURL(), sourceContent, targetAlias, targetURL, encKeyDB)
		}
	}(sourceURL, targetURL, copyURLsCh)
	return copyURLsCh
}

// makeCopyContentTypeC - CopyURLs content for copying.
func makeCopyContentTypeC(sourceAlias string, sourceURL ClientURL, sourceContent *ClientContent, targetAlias string, targetURL string, encKeyDB map[string][]prefixSSEPair) URLs {
	newSourceURL := sourceContent.URL
	pathSeparatorIndex := strings.LastIndex(sourceURL.Path, string(sourceURL.Separator))
	newSourceSuffix := filepath.ToSlash(newSourceURL.Path)
	if pathSeparatorIndex > 1 {
		sourcePrefix := filepath.ToSlash(sourceURL.Path[:pathSeparatorIndex])
		// do not preserve unix cp behavior when copying a filesytem dir to
		// objectstore.
		if sourceAlias == "" && targetAlias != "" {
			// Check if sourceURL.Path is a directory or not
			fileInfo, err := os.Stat(sourceURL.Path)
			if err != nil {
				return URLs{Error: probe.NewError(err)}
			}
			if fileInfo.IsDir() {
				sourcePrefix = filepath.ToSlash(sourceURL.Path)
			}
		}
		newSourceSuffix = strings.TrimPrefix(newSourceSuffix, sourcePrefix)
	}
	newTargetURL := urlJoinPath(targetURL, newSourceSuffix)
	return makeCopyContentTypeA(sourceAlias, sourceContent, targetAlias, newTargetURL, encKeyDB)
}

// MULTI-SOURCE - Type D: copy([](f|d...), d) -> []B
// prepareCopyURLsTypeE - prepares target and source clientURLs for copying.
func prepareCopyURLsTypeD(sourceURLs []string, targetURL string, isRecursive bool, encKeyDB map[string][]prefixSSEPair) <-chan URLs {
	copyURLsCh := make(chan URLs)
	go func(sourceURLs []string, targetURL string, copyURLsCh chan URLs) {
		defer close(copyURLsCh)
		for _, sourceURL := range sourceURLs {
			for cpURLs := range prepareCopyURLsTypeC(sourceURL, targetURL, isRecursive, encKeyDB) {
				copyURLsCh <- cpURLs
			}
		}
	}(sourceURLs, targetURL, copyURLsCh)
	return copyURLsCh
}

// prepareCopyURLs - prepares target and source clientURLs for copying.
func prepareCopyURLs(sourceURLs []string, targetURL string, isRecursive bool, encKeyDB map[string][]prefixSSEPair, olderThan, newerThan string) chan URLs {
	copyURLsCh := make(chan URLs)
	go func(sourceURLs []string, targetURL string, copyURLsCh chan URLs, encKeyDB map[string][]prefixSSEPair) {
		defer close(copyURLsCh)
		cpType, err := guessCopyURLType(sourceURLs, targetURL, isRecursive, encKeyDB)
		fatalIf(err.Trace(), "Unable to guess the type of copy operation.")

		switch cpType {
		case copyURLsTypeA:
			copyURLsCh <- prepareCopyURLsTypeA(sourceURLs[0], targetURL, encKeyDB)
		case copyURLsTypeB:
			copyURLsCh <- prepareCopyURLsTypeB(sourceURLs[0], targetURL, encKeyDB)
		case copyURLsTypeC:
			for cURLs := range prepareCopyURLsTypeC(sourceURLs[0], targetURL, isRecursive, encKeyDB) {
				copyURLsCh <- cURLs
			}
		case copyURLsTypeD:
			for cURLs := range prepareCopyURLsTypeD(sourceURLs, targetURL, isRecursive, encKeyDB) {
				copyURLsCh <- cURLs
			}
		default:
			copyURLsCh <- URLs{Error: errInvalidArgument().Trace(sourceURLs...)}
		}
	}(sourceURLs, targetURL, copyURLsCh, encKeyDB)

	finalCopyURLsCh := make(chan URLs)
	go func() {
		defer close(finalCopyURLsCh)
		for cpURLs := range copyURLsCh {
			// Skip objects older than --older-than parameter if specified
			if olderThan != "" && isOlder(cpURLs.SourceContent.Time, olderThan) {
				continue
			}

			// Skip objects newer than --newer-than parameter if specified
			if newerThan != "" && isNewer(cpURLs.SourceContent.Time, newerThan) {
				continue
			}

			finalCopyURLsCh <- cpURLs
		}
	}()

	return finalCopyURLsCh
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/heraware/cryptodev/clients"
	"github.com/spf13/cobra"
)

// createCmd represents the create command
var createCmd = &cobra.Command{
	Use:   "create",
	Short: "Create a new node - Example: cryptodev create bitcoin",
	Run: func(cmd *cobra.Command, args []string) {
		docker := clients.NewDockerClient()
		docker.CreateNode("bitcoin")
	},
}

func init() {
	RootCmd.AddCommand(createCmd)
}
<commit_msg>Create node with the defined argument<commit_after>package cmd

import (
	"github.com/heraware/cryptodev/clients"
	"github.com/spf13/cobra"
)

// createCmd represents the create command
var createCmd = &cobra.Command{
	Use:   "create",
	Short: "Create a new node - Example: cryptodev create bitcoin",
	Run: func(cmd *cobra.Command, args []string) {
		docker := clients.NewDockerClient()
		if len(args) > 0 {
			nodeName := args[0]
			docker.CreateNode(nodeName)
		}
	},
}

func init() {
	RootCmd.AddCommand(createCmd)
}
<|endoftext|>


<commit_before>// Copyright © 2017 The Kubicorn 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 cmd

import (
	//"github.com/spf13/pflag"
	"fmt"
	"os"
	"os/user"

	"github.com/kris-nova/kubicorn/apis/cluster"
	"github.com/kris-nova/kubicorn/cutil/logger"
	"github.com/kris-nova/kubicorn/cutil/namer"
	"github.com/kris-nova/kubicorn/profiles"
	"github.com/kris-nova/kubicorn/state"
	"github.com/kris-nova/kubicorn/state/fs"
	"github.com/spf13/cobra"
)

type CreateOptions struct {
	Options
	Profile string
}

var co = &CreateOptions{}

var createCmd = &cobra.Command{
	Use:   "create [NAME] [-p|--profile PROFILENAME]",
	Short: "Create a Kubicorn API model from a profile",
	Long: `Use this command to create a Kubicorn API model in a defined state store.

This command will create a cluster API model as a YAML manifest in a state store.
Once the API model has been created, a user can optionally change the model to their liking.
After a model is defined and configured properly, the user can then apply the model.`,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			co.Name = strEnvDef("KUBICORN_NAME", namer.RandomName())
		} else if len(args) > 1 {
			logger.Critical("Too many arguments.")
			os.Exit(1)
		} else {
			co.Name = args[0]
		}

		err := RunCreate(co)
		if err != nil {
			logger.Critical(err.Error())
			os.Exit(1)
		}

	},
}

func init() {
	createCmd.Flags().StringVarP(&co.StateStore, "state-store", "s", strEnvDef("KUBICORN_STATE_STORE", "fs"), "The state store type to use for the cluster")
	createCmd.Flags().StringVarP(&co.StateStorePath, "state-store-path", "S", strEnvDef("KUBICORN_STATE_STORE_PATH", "./_state"), "The state store path to use")
	createCmd.Flags().StringVarP(&co.Profile, "profile", "p", strEnvDef("KUBICORN_PROFILE", "azure"), "The cluster profile to use")

	//flagApplyAnnotations(createCmd, "name", "__kubicorn_parse_list")
	flagApplyAnnotations(createCmd, "profile", "__kubicorn_parse_profiles")

	RootCmd.AddCommand(createCmd)
}

type profileFunc func(name string) *cluster.Cluster

var alias = map[string]profileFunc{
	"amazon":       profiles.NewSimpleAmazonCluster,
	"aws":          profiles.NewSimpleAmazonCluster,
	"do":           profiles.NewSimpleDigitalOceanCluster,
	"digitalocean": profiles.NewSimpleDigitalOceanCluster,
	"do-centos":    profiles.CentosDigitalOceanCluster,
	"aws-centos":   profiles.CentosAmazonCluster,
}

func RunCreate(options *CreateOptions) error {

	// Create our cluster resource
	name := options.Name
	var cluster *cluster.Cluster
	if _, ok := alias[options.Profile]; ok {
		cluster = alias[options.Profile](name)
	} else {
		return fmt.Errorf("Invalid profile [%s]", options.Profile)
	}

	// Expand state store path
	// Todo (@kris-nova) please pull this into a filepath package or something
	options.StateStorePath = expandPath(options.StateStorePath)

	// Register state store
	var stateStore state.ClusterStorer
	switch options.StateStore {
	case "fs":
		logger.Info("Selected [fs] state store")
		stateStore = fs.NewFileSystemStore(&fs.FileSystemStoreOptions{
			BasePath:    options.StateStorePath,
			ClusterName: name,
		})
	}

	// Check if state store exists
	if stateStore.Exists() {
		return fmt.Errorf("State store [%s] exists, will not overwrite", name)
	}

	// Init new state store with the cluster resource
	err := stateStore.Commit(cluster)
	if err != nil {
		return fmt.Errorf("Unable to init state store: %v", err)
	}

	logger.Always("The state [%s/%s/cluster.yaml] has been created. You can edit the file, then run `kubicorn apply -n %s`", options.StateStorePath, name, name)
	return nil
}

func expandPath(path string) string {
	if path == "." {
		wd, err := os.Getwd()
		if err != nil {
			logger.Critical("Unable to get current working directory: %v", err)
			return ""
		}
		path = wd
	}
	if path == "~" {
		homeVar := os.Getenv("HOME")
		if homeVar == "" {
			homeUser, err := user.Current()
			if err != nil {
				logger.Critical("Unable to use user.Current() for user. Maybe a cross compile issue: %v", err)
				return ""
			}
			path = homeUser.HomeDir
		}
	}
	return path
}
<commit_msg>Fixed output run command<commit_after>// Copyright © 2017 The Kubicorn 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 cmd

import (
	//"github.com/spf13/pflag"
	"fmt"
	"os"
	"os/user"

	"github.com/kris-nova/kubicorn/apis/cluster"
	"github.com/kris-nova/kubicorn/cutil/logger"
	"github.com/kris-nova/kubicorn/cutil/namer"
	"github.com/kris-nova/kubicorn/profiles"
	"github.com/kris-nova/kubicorn/state"
	"github.com/kris-nova/kubicorn/state/fs"
	"github.com/spf13/cobra"
)

type CreateOptions struct {
	Options
	Profile string
}

var co = &CreateOptions{}

var createCmd = &cobra.Command{
	Use:   "create [NAME] [-p|--profile PROFILENAME]",
	Short: "Create a Kubicorn API model from a profile",
	Long: `Use this command to create a Kubicorn API model in a defined state store.

This command will create a cluster API model as a YAML manifest in a state store.
Once the API model has been created, a user can optionally change the model to their liking.
After a model is defined and configured properly, the user can then apply the model.`,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) == 0 {
			co.Name = strEnvDef("KUBICORN_NAME", namer.RandomName())
		} else if len(args) > 1 {
			logger.Critical("Too many arguments.")
			os.Exit(1)
		} else {
			co.Name = args[0]
		}

		err := RunCreate(co)
		if err != nil {
			logger.Critical(err.Error())
			os.Exit(1)
		}

	},
}

func init() {
	createCmd.Flags().StringVarP(&co.StateStore, "state-store", "s", strEnvDef("KUBICORN_STATE_STORE", "fs"), "The state store type to use for the cluster")
	createCmd.Flags().StringVarP(&co.StateStorePath, "state-store-path", "S", strEnvDef("KUBICORN_STATE_STORE_PATH", "./_state"), "The state store path to use")
	createCmd.Flags().StringVarP(&co.Profile, "profile", "p", strEnvDef("KUBICORN_PROFILE", "azure"), "The cluster profile to use")

	//flagApplyAnnotations(createCmd, "name", "__kubicorn_parse_list")
	flagApplyAnnotations(createCmd, "profile", "__kubicorn_parse_profiles")

	RootCmd.AddCommand(createCmd)
}

type profileFunc func(name string) *cluster.Cluster

var alias = map[string]profileFunc{
	"amazon":       profiles.NewSimpleAmazonCluster,
	"aws":          profiles.NewSimpleAmazonCluster,
	"do":           profiles.NewSimpleDigitalOceanCluster,
	"digitalocean": profiles.NewSimpleDigitalOceanCluster,
	"do-centos":    profiles.CentosDigitalOceanCluster,
	"aws-centos":   profiles.CentosAmazonCluster,
}

func RunCreate(options *CreateOptions) error {

	// Create our cluster resource
	name := options.Name
	var cluster *cluster.Cluster
	if _, ok := alias[options.Profile]; ok {
		cluster = alias[options.Profile](name)
	} else {
		return fmt.Errorf("Invalid profile [%s]", options.Profile)
	}

	// Expand state store path
	// Todo (@kris-nova) please pull this into a filepath package or something
	options.StateStorePath = expandPath(options.StateStorePath)

	// Register state store
	var stateStore state.ClusterStorer
	switch options.StateStore {
	case "fs":
		logger.Info("Selected [fs] state store")
		stateStore = fs.NewFileSystemStore(&fs.FileSystemStoreOptions{
			BasePath:    options.StateStorePath,
			ClusterName: name,
		})
	}

	// Check if state store exists
	if stateStore.Exists() {
		return fmt.Errorf("State store [%s] exists, will not overwrite", name)
	}

	// Init new state store with the cluster resource
	err := stateStore.Commit(cluster)
	if err != nil {
		return fmt.Errorf("Unable to init state store: %v", err)
	}

	logger.Always("The state [%s/%s/cluster.yaml] has been created. You can edit the file, then run `kubicorn apply %s`", options.StateStorePath, name, name)
	return nil
}

func expandPath(path string) string {
	if path == "." {
		wd, err := os.Getwd()
		if err != nil {
			logger.Critical("Unable to get current working directory: %v", err)
			return ""
		}
		path = wd
	}
	if path == "~" {
		homeVar := os.Getenv("HOME")
		if homeVar == "" {
			homeUser, err := user.Current()
			if err != nil {
				logger.Critical("Unable to use user.Current() for user. Maybe a cross compile issue: %v", err)
				return ""
			}
			path = homeUser.HomeDir
		}
	}
	return path
}
<|endoftext|>


<commit_before>package cmd

import (
	"context"
	"fmt"

	"github.com/nerdalize/nerd/pkg/kubevisor"
	transferstore "github.com/nerdalize/nerd/pkg/transfer/store"
	"github.com/nerdalize/nerd/svc"
	"github.com/pkg/errors"
)

var (
	// ErrNamespaceNotSet is returned when no namespace config is found in the kube config file.
	ErrNamespaceNotSet = errors.New("no cluster set, use `nerd login` to update your configuration")
	// ErrNotLoggedIn is returned when no oauth access token was found in the config file.
	ErrNotLoggedIn = errors.New("you're not logged in. Please login with `nerd login`")
	// ErrOverwriteWarning is returned when a user is trying to use the same name for input and output datasets
	ErrOverwriteWarning = errors.New("it is not possible to use the same name for input and output datasets, as it would overwrite your dataset")
)

// errShowHelp can be returned by commands to show the commands help message next to the error.
type errShowHelp string

func (e errShowHelp) Error() string { return string(e) }

// errShowUsage can be returned by commands to show usage.
type errShowUsage string

func (e errShowUsage) Error() string { return string(e) }

func renderServiceError(err error, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}

	err = errors.Cause(err)
	if err == context.Canceled {
		return errors.Errorf("%s: process was canceled", fmt.Errorf(format, args...))
	}

	switch {
	case kubevisor.IsInvalidNameErr(err):
		return errors.Errorf("%s: invalid name, must consist of alphanumeric characters, '-' or '.'", fmt.Errorf(format, args...))
	case kubevisor.IsDeadlineErr(err):
		return errors.Errorf("%s: action took to long to complete, try again or check your internet connection", fmt.Errorf(format, args...))
	case kubevisor.IsNetworkErr(err):
		return errors.Errorf("%s: failed to reach the cluster, make sure you're connected to the internet and try again. Also, you can check the status page: http://status.nerdalize.com/", fmt.Errorf(format, args...))
	case kubevisor.IsNotExistsErr(err):
		return errors.Errorf("%s: it does not exist", fmt.Errorf(format, args...))
	case kubevisor.IsKubernetesErr(err):
		return errors.Errorf("%s: cluster failed to perform action: %v", fmt.Errorf(format, args...), err)
	case kubevisor.IsAlreadyExistsErr(err):
		return errors.Errorf("%s: this name is already in use, please use the list command to see what is already there", fmt.Errorf(format, args...))
	case kubevisor.IsNamespaceNotExistsErr(err):
		return errors.Errorf("%s: the namespace does not exist or you have no access. If the problem persists, please contact mayday@nerdalize.com.", fmt.Errorf(format, args...))
	case kubevisor.IsServiceUnavailableErr(err):
		return errors.Errorf("%s: cluster is currently unable to receive requests, try again later. Also, you can check the status page: http://status.nerdalize.com/", fmt.Errorf(format, args...))
	case kubevisor.IsUnauthorizedErr(err):
		return errors.Errorf("%s: you do not have permission to perform this action", fmt.Errorf(format, args...))
	case svc.IsRaceConditionErr(err):
		return errors.Errorf("%s: another process caused your action to fail, please try again", fmt.Errorf(format, args...))
	case errors.Cause(err) == ErrNamespaceNotSet:
		return ErrNamespaceNotSet
	case errors.Cause(err) == ErrNotLoggedIn:
		return ErrNotLoggedIn
	case errors.Cause(err) == transferstore.ErrObjectNotExists:
		return errors.Errorf("%s: dataset data is not available, it might still be uploading, check back again later", fmt.Errorf(format, args...))
	default:
		return errors.Wrapf(err, format, args...)
	}
}

func renderConfigError(err error, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}

	switch {
	case errors.Cause(err) == ErrNotLoggedIn:
		return ErrNotLoggedIn
	case errors.Cause(err) == ErrNamespaceNotSet:
		return ErrNamespaceNotSet
	default:
		return err
	}
}
<commit_msg>return specific error for invalid refresh token<commit_after>package cmd

import (
	"context"
	"fmt"
	"strings"

	"github.com/nerdalize/nerd/pkg/kubevisor"
	transferstore "github.com/nerdalize/nerd/pkg/transfer/store"
	"github.com/nerdalize/nerd/svc"
	"github.com/pkg/errors"
)

var (
	// ErrNamespaceNotSet is returned when no namespace config is found in the kube config file.
	ErrNamespaceNotSet = errors.New("no cluster set, use `nerd login` to update your configuration")
	// ErrNotLoggedIn is returned when no oauth access token was found in the config file.
	ErrNotLoggedIn = errors.New("you're not logged in. Please login with `nerd login`")
	// ErrOverwriteWarning is returned when a user is trying to use the same name for input and output datasets
	ErrOverwriteWarning = errors.New("it is not possible to use the same name for input and output datasets, as it would overwrite your dataset")
)

// errShowHelp can be returned by commands to show the commands help message next to the error.
type errShowHelp string

func (e errShowHelp) Error() string { return string(e) }

// errShowUsage can be returned by commands to show usage.
type errShowUsage string

func (e errShowUsage) Error() string { return string(e) }

func renderServiceError(err error, format string, args ...interface{}) error {

	if err == nil {
		return nil
	}

	err = errors.Cause(err)
	if err == context.Canceled {
		return errors.Errorf("%s: process was canceled", fmt.Errorf(format, args...))
	}

	switch {
	case strings.Contains(err.Error(), "failed to refresh token"):
		return errors.Errorf("Please check your permissions or try to login again.")
	case kubevisor.IsInvalidNameErr(err):
		return errors.Errorf("%s: invalid name, must consist of alphanumeric characters, '-' or '.'", fmt.Errorf(format, args...))
	case kubevisor.IsDeadlineErr(err):
		return errors.Errorf("%s: action took to long to complete, try again or check your internet connection", fmt.Errorf(format, args...))
	case kubevisor.IsNetworkErr(err):
		return errors.Errorf("%s: failed to reach the cluster, make sure you're connected to the internet and try again. Also, you can check the status page: http://status.nerdalize.com/", fmt.Errorf(format, args...))
	case kubevisor.IsNotExistsErr(err):
		return errors.Errorf("%s: it does not exist", fmt.Errorf(format, args...))
	case kubevisor.IsKubernetesErr(err):
		return errors.Errorf("%s: cluster failed to perform action: %v", fmt.Errorf(format, args...), err)
	case kubevisor.IsAlreadyExistsErr(err):
		return errors.Errorf("%s: this name is already in use, please use the list command to see what is already there", fmt.Errorf(format, args...))
	case kubevisor.IsNamespaceNotExistsErr(err):
		return errors.Errorf("%s: the namespace does not exist or you have no access. If the problem persists, please contact mayday@nerdalize.com.", fmt.Errorf(format, args...))
	case kubevisor.IsServiceUnavailableErr(err):
		return errors.Errorf("%s: cluster is currently unable to receive requests, try again later. Also, you can check the status page: http://status.nerdalize.com/", fmt.Errorf(format, args...))
	case kubevisor.IsUnauthorizedErr(err):
		return errors.Errorf("%s: you do not have permission to perform this action", fmt.Errorf(format, args...))
	case svc.IsRaceConditionErr(err):
		return errors.Errorf("%s: another process caused your action to fail, please try again", fmt.Errorf(format, args...))
	case errors.Cause(err) == ErrNamespaceNotSet:
		return ErrNamespaceNotSet
	case errors.Cause(err) == ErrNotLoggedIn:
		return ErrNotLoggedIn
	case errors.Cause(err) == transferstore.ErrObjectNotExists:
		return errors.Errorf("%s: dataset data is not available, it might still be uploading, check back again later", fmt.Errorf(format, args...))
	default:
		return errors.Wrapf(err, format, args...)
	}
}

func renderConfigError(err error, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}

	switch {
	case errors.Cause(err) == ErrNotLoggedIn:
		return ErrNotLoggedIn
	case errors.Cause(err) == ErrNamespaceNotSet:
		return ErrNamespaceNotSet
	default:
		return err
	}
}
<|endoftext|>


<commit_before>// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// 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 (
	"fmt"

	"github.com/oberd/ecsy/ecs"
	"github.com/spf13/cobra"
)

// eventsCmd represents the events command
var eventsCmd = &cobra.Command{
	Use:   "events",
	Short: "Show recent events for a service in a cluster",
	Long:  `Show recent events for a service in a cluster`,
	Run: func(cmd *cobra.Command, args []string) {
		svc, err := ecs.FindService(args[0], args[1])
		failOnError(err, "Error finding service")
		out := ""
		for _, event := range svc.Events {
			out = fmt.Sprintf("[%v] %s\n", *event.CreatedAt, *event.Message) + out
		}
		fmt.Print(out)
	},
}

func init() {
	RootCmd.AddCommand(eventsCmd)
}
<commit_msg>Remove comments<commit_after>package cmd

import (
	"fmt"

	"github.com/oberd/ecsy/ecs"
	"github.com/spf13/cobra"
)

// eventsCmd represents the events command
var eventsCmd = &cobra.Command{
	Use:   "events",
	Short: "Show recent events for a service in a cluster",
	Long:  `Show recent events for a service in a cluster`,
	Run: func(cmd *cobra.Command, args []string) {
		svc, err := ecs.FindService(args[0], args[1])
		failOnError(err, "Error finding service")
		out := ""
		for _, event := range svc.Events {
			out = fmt.Sprintf("[%v] %s\n", *event.CreatedAt, *event.Message) + out
		}
		fmt.Print(out)
	},
}

func init() {
	RootCmd.AddCommand(eventsCmd)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"

	"github.com/influxdata/toml"
	"github.com/spf13/cobra"

	"github.com/ostrost/ostent/flags"
	"github.com/ostrost/ostent/internal"
	"github.com/ostrost/ostent/internal/agent"
	"github.com/ostrost/ostent/internal/config"
	"github.com/ostrost/ostent/internal/models"
	"github.com/ostrost/ostent/ostent"

	// plugging outputs:
	_ "github.com/influxdata/telegraf/plugins/outputs/graphite"
	_ "github.com/influxdata/telegraf/plugins/outputs/influxdb"
	_ "github.com/influxdata/telegraf/plugins/outputs/librato"

	_ "github.com/ostrost/ostent/internal/plugins/outputs/ostent" // "ostent" output

	// plugging inputs:
	_ "github.com/influxdata/telegraf/plugins/inputs/system" // "{cpu,disk,mem,swap}" inputs

	_ "github.com/ostrost/ostent/procstat_ostent" // "procstat_ostent" input
	_ "github.com/ostrost/ostent/system_ostent"   // "{net,system}_ostent" inputs
)

var (
	persistentPostRuns runs // a list of funcs to be cobra.Command's PersistentPostRunE.
	persistentPreRuns  runs // a list of funcs to be cobra.Command's PersistentPreRunEE.
	preRuns            runs // a list of funcs to be cobra.Command's PreRunE.
)

type runs struct {
	mutex sync.Mutex     // protect everything e.g. list
	list  []func() error // the list to have
}

func (rs *runs) add(f func() error) {
	rs.mutex.Lock()
	defer rs.mutex.Unlock()
	rs.list = append(rs.list, f)
}

func (rs *runs) runE(*cobra.Command, []string) error {
	rs.mutex.Lock()
	defer rs.mutex.Unlock()
	for _, run := range rs.list {
		if err := run(); err != nil {
			return err
		}
	}
	return nil
}

// Bind is flag value.
var Bind = flags.NewBind("", 8050)

func initFlags() {
	rconfig := config.NewConfig()

	persistentPreRuns.add(versionRun)
	RootCmd.PersistentFlags().BoolVar(&versionFlag, "version", false, "Print version and exit")

	RootCmd.Flags().VarP(&Bind, "bind", "b", "Bind `address`")

	defaultInterval := rconfig.Agent.Interval.Duration.String()
	intervals := struct{ Agent string }{}
	/*
		intervals := struct{ Agent, CPU, Disk, Mem, NetOstent, ProcstatOstent, Swap string }{}
		for _, v := range []struct {
			pointer *string
			name    string
			usage   string
		}{
			{&intervals.CPU, "input-interval-cpu", "Interval for input: cpu"},
			{&intervals.Disk, "input-interval-disk", "Interval for input: disk"},
			{&intervals.Mem, "input-interval-mem", "Interval for input: mem"},
			{&intervals.NetOstent, "input-interval-net-ostent", "Interval for input: net_ostent"},
			{&intervals.ProcstatOstent, "input-interval-procstat-ostent", "Interval for input: procstat_ostent"},
			{&intervals.Swap, "input-interval-swap", "Interval for input: swap"},
			// ...
		} {
			RootCmd.Flags().StringVar(v.pointer, v.name, defaultInterval, v.usage)
		}
	*/
	RootCmd.Flags().StringVarP(&intervals.Agent, "interval", "d", defaultInterval, "Interval for agent and inputs")

	_ = defaultInterval
	/*
		preRuns.add(func() error {
			if intervals.Agent != defaultInterval {
				var interval internal.Duration
				_ = interval.UnmarshalTOML([]byte(fmt.Sprintf("%q", intervals.Agent)))
				// TODO set rconfig.Agent.{Flush,}Interval, rconfig.Outputs["system_ostent"].Interval
			}

			for _, v := range []struct {
				pointer **string
				value   string
			}{
				{&rconfig.Inputs.CPU.Interval, intervals.CPU},
				{&rconfig.Inputs.Disk.Interval, intervals.Disk},
				{&rconfig.Inputs.Mem.Interval, intervals.Mem},
				{&rconfig.Inputs.Net_ostent.Interval, intervals.NetOstent},
				{&rconfig.Inputs.Procstat_ostent.Interval, intervals.ProcstatOstent},
				{&rconfig.Inputs.Swap.Interval, intervals.Swap},
			} {
				if v.value != defaultInterval {
					*v.pointer = new(string)
					**v.pointer = v.value
				}
			}
			return nil
		})
	*/

	preRuns.add(func() error {
		ostent.AddBackground(func() {
			if err := mainAgent(rconfig); err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		})
		return nil
	})
}

func mainAgent(rconfig *config.Config) error {
	reload := make(chan bool, 1)
	reload <- true
	for <-reload {
		reload <- false

		c := rconfig // config.NewConfig() // TODO rconfig copy
		c.Agent.Quiet = true

		if tab, err := readConfig(rconfig); err != nil {
			return err
		} else if tab != nil {
			if err := c.LoadTable("/runtime/config", tab); err != nil {
				return err
			}
		}

		if text, err := printableConfig(rconfig); err != nil {
			return err
		} else {
			log.Printf("Runtime config:\n%s", text)
			/*
				var system_ostent *models.RunningInput
				for _, ri := range rconfig.Inputs {
					if ri.Name == "system_ostent" {
						system_ostent = ri
					}
				}
				log.Printf("ins...._system_ostent.interval = %q\n",
					system_ostent.Config.Interval) */
		}

		ag, err := agent.NewAgent(c)
		if err != nil {
			return err
		}

		if err := ag.Connect(); err != nil {
			return err
		}

		shutdown := make(chan struct{})
		signals := make(chan os.Signal)
		signal.Notify(signals, os.Interrupt, syscall.SIGHUP)
		go func() {
			sig := <-signals
			if sig == os.Interrupt {
				close(shutdown)
			}
			if sig == syscall.SIGHUP {
				log.Printf("Reloading config\n")
				<-reload
				reload <- true
				close(shutdown)
			}
		}()

		if err := ag.Run(shutdown); err != nil {
			return err
		}
	}
	return nil
}

func printableConfigText(text string) string {
	lines := strings.Split(text, "\n")
	for _, replace := range [][2]string{
		{"password = ", `"PASSWORD"`},
		{"api_token = ", `"API_TOKEN"`},
	} {
		for i := range lines {
			if j := strings.Index(lines[i], replace[0]); j != -1 {
				lines[i] = lines[i][:j] + replace[0] + replace[1]
			}
		}
	}
	return strings.Join(lines, "\n")
}

func printableConfig(rconfig *config.Config) (string, error) {
	agtext, err := toml.Marshal(struct{ Agent *config.AgentConfig }{rconfig.Agent})
	if err != nil {
		return "", err
	}

	text := string(agtext) + "[inputs]\n"
	for _, in := range rconfig.Inputs {
		subtext, err := printableTable("inputs", in.Name, in.Input,
			printableInput(in.Config), printableFilter(in.Config.Filter))
		if err != nil {
			return "", err
		}
		text += subtext
	}

	text += "[outputs]\n"
	for _, out := range rconfig.Outputs {
		subtext, err := printableTable("outputs", out.Name, out.Output,
			nil, printableFilter(out.Config.Filter))
		if err != nil {
			return "", err
		}
		text += subtext
	}

	return printableConfigText(text), nil
}

func printableTable(upname, name string, in1 interface{},
	in2 *printInput, in3 *printFilter) (string, error) {
	intext1, err := toml.Marshal(in1)
	if err != nil {
		return "", err
	}
	var intext2 []byte
	if in2 != nil {
		intext2, err = toml.Marshal(in2)
		if err != nil {
			return "", err
		}
	}
	var intext3 []byte
	if in3 != nil {
		intext3, err = toml.Marshal(in3)
		if err != nil {
			return "", err
		}
	}
	lines := strings.Split(string(intext1)+string(intext2)+string(intext3), "\n")
	for i := range lines {
		if lines[i] != "" {
			lines[i] = "        " + lines[i]
		}
	}
	return "    [" + upname + "." + name + "]\n" + strings.Join(lines, "\n"), nil
}

type printInput struct {
	NameOverride      string             `toml:",omitempty"`
	MeasurementPrefix string             `toml:",omitempty"`
	MeasurementSuffix string             `toml:",omitempty"`
	Tags              *map[string]string `toml:",omitempty"`
	Interval          internal.Duration  `toml:",omitempty"`
}

func printableInput(ic *models.InputConfig) *printInput {
	p := printInput{
		NameOverride:      ic.NameOverride,
		MeasurementPrefix: ic.MeasurementPrefix,
		MeasurementSuffix: ic.MeasurementSuffix,
		Interval:          internal.Duration{Duration: ic.Interval},
	}
	if ic.Tags != nil && len(ic.Tags) > 0 {
		p.Tags = &ic.Tags
	}
	if p == (printInput{}) {
		return nil
	}
	return &p
}

type printFilter struct {
	Filter struct {
		NameDrop   *[]string           `toml:",omitempty"`
		NamePass   *[]string           `toml:",omitempty"`
		FieldDrop  *[]string           `toml:",omitempty"`
		FieldPass  *[]string           `toml:",omitempty"`
		TagDrop    *[]models.TagFilter `toml:",omitempty"`
		TagPass    *[]models.TagFilter `toml:",omitempty"`
		TagExclude *[]string           `toml:",omitempty"`
		TagInclude *[]string           `toml:",omitempty"`
	}
}

func printableFilter(f models.Filter) *printFilter {
	if !f.IsActive {
		return nil
	}
	var p printFilter
	for _, pair := range []struct {
		in  *[]string
		out **[]string
	}{
		{&f.NameDrop, &p.Filter.NameDrop},
		{&f.NamePass, &p.Filter.NamePass},
		{&f.FieldDrop, &p.Filter.FieldDrop},
		{&f.FieldPass, &p.Filter.FieldPass},
		{&f.TagExclude, &p.Filter.TagExclude},
		{&f.TagInclude, &p.Filter.TagInclude},
	} {
		if len(*pair.in) > 0 {
			*pair.out = pair.in
		}
	}
	for _, pair := range []struct {
		in  *[]models.TagFilter
		out **[]models.TagFilter
	}{
		{&f.TagDrop, &p.Filter.TagDrop},
		{&f.TagPass, &p.Filter.TagPass},
	} {
		if len(*pair.in) > 0 {
			*pair.out = pair.in
		}
	}
	return &p
}
<commit_msg>config printing: filter out empty values lines<commit_after>package cmd

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"

	"github.com/influxdata/toml"
	"github.com/spf13/cobra"

	"github.com/ostrost/ostent/flags"
	"github.com/ostrost/ostent/internal"
	"github.com/ostrost/ostent/internal/agent"
	"github.com/ostrost/ostent/internal/config"
	"github.com/ostrost/ostent/internal/models"
	"github.com/ostrost/ostent/ostent"

	// plugging outputs:
	_ "github.com/influxdata/telegraf/plugins/outputs/graphite"
	_ "github.com/influxdata/telegraf/plugins/outputs/influxdb"
	_ "github.com/influxdata/telegraf/plugins/outputs/librato"

	_ "github.com/ostrost/ostent/internal/plugins/outputs/ostent" // "ostent" output

	// plugging inputs:
	_ "github.com/influxdata/telegraf/plugins/inputs/system" // "{cpu,disk,mem,swap}" inputs

	_ "github.com/ostrost/ostent/procstat_ostent" // "procstat_ostent" input
	_ "github.com/ostrost/ostent/system_ostent"   // "{net,system}_ostent" inputs
)

var (
	persistentPostRuns runs // a list of funcs to be cobra.Command's PersistentPostRunE.
	persistentPreRuns  runs // a list of funcs to be cobra.Command's PersistentPreRunEE.
	preRuns            runs // a list of funcs to be cobra.Command's PreRunE.
)

type runs struct {
	mutex sync.Mutex     // protect everything e.g. list
	list  []func() error // the list to have
}

func (rs *runs) add(f func() error) {
	rs.mutex.Lock()
	defer rs.mutex.Unlock()
	rs.list = append(rs.list, f)
}

func (rs *runs) runE(*cobra.Command, []string) error {
	rs.mutex.Lock()
	defer rs.mutex.Unlock()
	for _, run := range rs.list {
		if err := run(); err != nil {
			return err
		}
	}
	return nil
}

// Bind is flag value.
var Bind = flags.NewBind("", 8050)

func initFlags() {
	rconfig := config.NewConfig()

	persistentPreRuns.add(versionRun)
	RootCmd.PersistentFlags().BoolVar(&versionFlag, "version", false, "Print version and exit")

	RootCmd.Flags().VarP(&Bind, "bind", "b", "Bind `address`")

	defaultInterval := rconfig.Agent.Interval.Duration.String()
	intervals := struct{ Agent string }{}
	/*
		intervals := struct{ Agent, CPU, Disk, Mem, NetOstent, ProcstatOstent, Swap string }{}
		for _, v := range []struct {
			pointer *string
			name    string
			usage   string
		}{
			{&intervals.CPU, "input-interval-cpu", "Interval for input: cpu"},
			{&intervals.Disk, "input-interval-disk", "Interval for input: disk"},
			{&intervals.Mem, "input-interval-mem", "Interval for input: mem"},
			{&intervals.NetOstent, "input-interval-net-ostent", "Interval for input: net_ostent"},
			{&intervals.ProcstatOstent, "input-interval-procstat-ostent", "Interval for input: procstat_ostent"},
			{&intervals.Swap, "input-interval-swap", "Interval for input: swap"},
			// ...
		} {
			RootCmd.Flags().StringVar(v.pointer, v.name, defaultInterval, v.usage)
		}
	*/
	RootCmd.Flags().StringVarP(&intervals.Agent, "interval", "d", defaultInterval, "Interval for agent and inputs")

	_ = defaultInterval
	/*
		preRuns.add(func() error {
			if intervals.Agent != defaultInterval {
				var interval internal.Duration
				_ = interval.UnmarshalTOML([]byte(fmt.Sprintf("%q", intervals.Agent)))
				// TODO set rconfig.Agent.{Flush,}Interval, rconfig.Outputs["system_ostent"].Interval
			}

			for _, v := range []struct {
				pointer **string
				value   string
			}{
				{&rconfig.Inputs.CPU.Interval, intervals.CPU},
				{&rconfig.Inputs.Disk.Interval, intervals.Disk},
				{&rconfig.Inputs.Mem.Interval, intervals.Mem},
				{&rconfig.Inputs.Net_ostent.Interval, intervals.NetOstent},
				{&rconfig.Inputs.Procstat_ostent.Interval, intervals.ProcstatOstent},
				{&rconfig.Inputs.Swap.Interval, intervals.Swap},
			} {
				if v.value != defaultInterval {
					*v.pointer = new(string)
					**v.pointer = v.value
				}
			}
			return nil
		})
	*/

	preRuns.add(func() error {
		ostent.AddBackground(func() {
			if err := mainAgent(rconfig); err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		})
		return nil
	})
}

func mainAgent(rconfig *config.Config) error {
	reload := make(chan bool, 1)
	reload <- true
	for <-reload {
		reload <- false

		c := rconfig // config.NewConfig() // TODO rconfig copy
		c.Agent.Quiet = true

		if tab, err := readConfig(rconfig); err != nil {
			return err
		} else if tab != nil {
			if err := c.LoadTable("/runtime/config", tab); err != nil {
				return err
			}
		}

		if text, err := printableConfig(rconfig); err != nil {
			return err
		} else {
			log.Printf("Runtime config:\n%s", text)
			/*
				var system_ostent *models.RunningInput
				for _, ri := range rconfig.Inputs {
					if ri.Name == "system_ostent" {
						system_ostent = ri
					}
				}
				log.Printf("ins...._system_ostent.interval = %q\n",
					system_ostent.Config.Interval) */
		}

		ag, err := agent.NewAgent(c)
		if err != nil {
			return err
		}

		if err := ag.Connect(); err != nil {
			return err
		}

		shutdown := make(chan struct{})
		signals := make(chan os.Signal)
		signal.Notify(signals, os.Interrupt, syscall.SIGHUP)
		go func() {
			sig := <-signals
			if sig == os.Interrupt {
				close(shutdown)
			}
			if sig == syscall.SIGHUP {
				log.Printf("Reloading config\n")
				<-reload
				reload <- true
				close(shutdown)
			}
		}()

		if err := ag.Run(shutdown); err != nil {
			return err
		}
	}
	return nil
}

func printableConfigText(text string) string {
	lines := strings.Split(text, "\n")
	var newlines []string
rangelines:
	for i := range lines {
		for _, suffix := range []string{
			" = 0",
			` = ""`,
			" = []",
			" = false",
		} {
			if strings.HasSuffix(lines[i], suffix) {
				continue rangelines
			}
		}

		for _, replace := range [][2]string{
			{"password = ", `"PASSWORD"`},
			{"api_token = ", `"API_TOKEN"`},
		} {
			if j := strings.Index(lines[i], replace[0]); j != -1 {
				lines[i] = lines[i][:j] + replace[0] + replace[1]
			}
		}
		newlines = append(newlines, lines[i])
	}
	return strings.Join(newlines, "\n")
}

func printableConfig(rconfig *config.Config) (string, error) {
	agtext, err := toml.Marshal(struct{ Agent *config.AgentConfig }{rconfig.Agent})
	if err != nil {
		return "", err
	}

	text := string(agtext) + "[inputs]\n"
	for _, in := range rconfig.Inputs {
		subtext, err := printableTable("inputs", in.Name, in.Input,
			printableInput(in.Config), printableFilter(in.Config.Filter))
		if err != nil {
			return "", err
		}
		text += subtext
	}

	text += "[outputs]\n"
	for _, out := range rconfig.Outputs {
		subtext, err := printableTable("outputs", out.Name, out.Output,
			nil, printableFilter(out.Config.Filter))
		if err != nil {
			return "", err
		}
		text += subtext
	}

	return printableConfigText(text), nil
}

func printableTable(upname, name string, in1 interface{},
	in2 *printInput, in3 *printFilter) (string, error) {
	intext1, err := toml.Marshal(in1)
	if err != nil {
		return "", err
	}
	var intext2 []byte
	if in2 != nil {
		intext2, err = toml.Marshal(in2)
		if err != nil {
			return "", err
		}
	}
	var intext3 []byte
	if in3 != nil {
		intext3, err = toml.Marshal(in3)
		if err != nil {
			return "", err
		}
	}
	lines := strings.Split(string(intext1)+string(intext2)+string(intext3), "\n")
	for i := range lines {
		if lines[i] != "" {
			lines[i] = "        " + lines[i]
		}
	}
	return "    [" + upname + "." + name + "]\n" + strings.Join(lines, "\n"), nil
}

type printInput struct {
	NameOverride      string             `toml:",omitempty"`
	MeasurementPrefix string             `toml:",omitempty"`
	MeasurementSuffix string             `toml:",omitempty"`
	Tags              *map[string]string `toml:",omitempty"`
	Interval          internal.Duration  `toml:",omitempty"`
}

func printableInput(ic *models.InputConfig) *printInput {
	p := printInput{
		NameOverride:      ic.NameOverride,
		MeasurementPrefix: ic.MeasurementPrefix,
		MeasurementSuffix: ic.MeasurementSuffix,
		Interval:          internal.Duration{Duration: ic.Interval},
	}
	if ic.Tags != nil && len(ic.Tags) > 0 {
		p.Tags = &ic.Tags
	}
	if p == (printInput{}) {
		return nil
	}
	return &p
}

type printFilter struct {
	Filter struct {
		NameDrop   *[]string           `toml:",omitempty"`
		NamePass   *[]string           `toml:",omitempty"`
		FieldDrop  *[]string           `toml:",omitempty"`
		FieldPass  *[]string           `toml:",omitempty"`
		TagDrop    *[]models.TagFilter `toml:",omitempty"`
		TagPass    *[]models.TagFilter `toml:",omitempty"`
		TagExclude *[]string           `toml:",omitempty"`
		TagInclude *[]string           `toml:",omitempty"`
	}
}

func printableFilter(f models.Filter) *printFilter {
	if !f.IsActive {
		return nil
	}
	var p printFilter
	for _, pair := range []struct {
		in  *[]string
		out **[]string
	}{
		{&f.NameDrop, &p.Filter.NameDrop},
		{&f.NamePass, &p.Filter.NamePass},
		{&f.FieldDrop, &p.Filter.FieldDrop},
		{&f.FieldPass, &p.Filter.FieldPass},
		{&f.TagExclude, &p.Filter.TagExclude},
		{&f.TagInclude, &p.Filter.TagInclude},
	} {
		if len(*pair.in) > 0 {
			*pair.out = pair.in
		}
	}
	for _, pair := range []struct {
		in  *[]models.TagFilter
		out **[]models.TagFilter
	}{
		{&f.TagDrop, &p.Filter.TagDrop},
		{&f.TagPass, &p.Filter.TagPass},
	} {
		if len(*pair.in) > 0 {
			*pair.out = pair.in
		}
	}
	return &p
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Holt and The Caddy 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 reverseproxy

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig"
	"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
	caddycmd "github.com/caddyserver/caddy/v2/cmd"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp/headers"
	"github.com/mholt/certmagic"
)

func init() {
	caddycmd.RegisterCommand(caddycmd.Command{
		Name:  "reverse-proxy",
		Func:  cmdReverseProxy,
		Usage: "[--from <addr>] [--to <addr>]",
		Short: "A quick and production-ready reverse proxy",
		Long: `
A simple but production-ready reverse proxy. Useful for quick deployments,
demos, and development.

Simply shuttles HTTP traffic from the --from address to the --to address.

If the --from address has a domain name, Caddy will attempt to serve the
proxy over HTTPS with a certificate.
`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("file-server", flag.ExitOnError)
			fs.String("from", "", "Address to receive traffic on")
			fs.String("to", "", "Upstream address to proxy traffic to")
			return fs
		}(),
	})
}

func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
	from := fs.String("from")
	to := fs.String("to")

	if from == "" {
		from = "localhost:" + httpcaddyfile.DefaultPort
	}

	// URLs need a scheme in order to parse successfully
	if !strings.Contains(from, "://") {
		from = "http://" + from
	}
	if !strings.Contains(to, "://") {
		to = "http://" + to
	}

	fromURL, err := url.Parse(from)
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("parsing 'from' URL: %v", err)
	}
	toURL, err := url.Parse(to)
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("parsing 'to' URL: %v", err)
	}

	ht := HTTPTransport{}
	if toURL.Scheme == "https" {
		ht.TLS = new(TLSConfig)
	}

	handler := Handler{
		TransportRaw: caddyconfig.JSONModuleObject(ht, "protocol", "http", nil),
		Upstreams:    UpstreamPool{{Dial: toURL.Host}},
		Headers: &headers.Handler{
			Request: &headers.HeaderOps{
				Set: http.Header{
					"Host": []string{"{http.handlers.reverse_proxy.upstream.host}"},
				},
			},
		},
	}

	route := caddyhttp.Route{
		HandlersRaw: []json.RawMessage{
			caddyconfig.JSONModuleObject(handler, "handler", "reverse_proxy", nil),
		},
	}
	urlHost := fromURL.Hostname()
	if urlHost != "" {
		route.MatcherSetsRaw = []map[string]json.RawMessage{
			map[string]json.RawMessage{
				"host": caddyconfig.JSON(caddyhttp.MatchHost{urlHost}, nil),
			},
		}
	}

	listen := ":80"
	if urlPort := fromURL.Port(); urlPort != "" {
		listen = ":" + urlPort
	} else if certmagic.HostQualifies(urlHost) {
		listen = ":443"
	}

	server := &caddyhttp.Server{
		Routes: caddyhttp.RouteList{route},
		Listen: []string{listen},
	}

	httpApp := caddyhttp.App{
		Servers: map[string]*caddyhttp.Server{"proxy": server},
	}

	cfg := &caddy.Config{
		AppsRaw: map[string]json.RawMessage{
			"http": caddyconfig.JSON(httpApp, nil),
		},
	}

	err = caddy.Run(cfg)
	if err != nil {
		return caddy.ExitCodeFailedStartup, err
	}

	fmt.Printf("Caddy 2 proxying from %s to %s\n", fromURL, toURL)

	select {}
}
<commit_msg>reverse_proxy: Add port to upstream address if only implied in scheme<commit_after>// Copyright 2015 Matthew Holt and The Caddy 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 reverseproxy

import (
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/url"
	"strings"

	"github.com/caddyserver/caddy/v2"
	"github.com/caddyserver/caddy/v2/caddyconfig"
	"github.com/caddyserver/caddy/v2/caddyconfig/httpcaddyfile"
	caddycmd "github.com/caddyserver/caddy/v2/cmd"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp"
	"github.com/caddyserver/caddy/v2/modules/caddyhttp/headers"
	"github.com/mholt/certmagic"
)

func init() {
	caddycmd.RegisterCommand(caddycmd.Command{
		Name:  "reverse-proxy",
		Func:  cmdReverseProxy,
		Usage: "[--from <addr>] [--to <addr>]",
		Short: "A quick and production-ready reverse proxy",
		Long: `
A simple but production-ready reverse proxy. Useful for quick deployments,
demos, and development.

Simply shuttles HTTP traffic from the --from address to the --to address.

If the --from address has a domain name, Caddy will attempt to serve the
proxy over HTTPS with a certificate.
`,
		Flags: func() *flag.FlagSet {
			fs := flag.NewFlagSet("file-server", flag.ExitOnError)
			fs.String("from", "", "Address to receive traffic on")
			fs.String("to", "", "Upstream address to proxy traffic to")
			return fs
		}(),
	})
}

func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
	from := fs.String("from")
	to := fs.String("to")

	if from == "" {
		from = "localhost:" + httpcaddyfile.DefaultPort
	}

	// URLs need a scheme in order to parse successfully
	if !strings.Contains(from, "://") {
		from = "http://" + from
	}
	if !strings.Contains(to, "://") {
		to = "http://" + to
	}

	fromURL, err := url.Parse(from)
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("parsing 'from' URL: %v", err)
	}
	toURL, err := url.Parse(to)
	if err != nil {
		return caddy.ExitCodeFailedStartup, fmt.Errorf("parsing 'to' URL: %v", err)
	}

	if toURL.Port() == "" {
		toPort := "80"
		if toURL.Scheme == "https" {
			toPort = "443"
		}
		toURL.Host = net.JoinHostPort(toURL.Host, toPort)
	}

	ht := HTTPTransport{}
	if toURL.Scheme == "https" {
		ht.TLS = new(TLSConfig)
	}

	handler := Handler{
		TransportRaw: caddyconfig.JSONModuleObject(ht, "protocol", "http", nil),
		Upstreams:    UpstreamPool{{Dial: toURL.Host}},
		Headers: &headers.Handler{
			Request: &headers.HeaderOps{
				Set: http.Header{
					"Host": []string{"{http.handlers.reverse_proxy.upstream.host}"},
				},
			},
		},
	}

	route := caddyhttp.Route{
		HandlersRaw: []json.RawMessage{
			caddyconfig.JSONModuleObject(handler, "handler", "reverse_proxy", nil),
		},
	}
	urlHost := fromURL.Hostname()
	if urlHost != "" {
		route.MatcherSetsRaw = []map[string]json.RawMessage{
			map[string]json.RawMessage{
				"host": caddyconfig.JSON(caddyhttp.MatchHost{urlHost}, nil),
			},
		}
	}

	listen := ":80"
	if urlPort := fromURL.Port(); urlPort != "" {
		listen = ":" + urlPort
	} else if certmagic.HostQualifies(urlHost) {
		listen = ":443"
	}

	server := &caddyhttp.Server{
		Routes: caddyhttp.RouteList{route},
		Listen: []string{listen},
	}

	httpApp := caddyhttp.App{
		Servers: map[string]*caddyhttp.Server{"proxy": server},
	}

	cfg := &caddy.Config{
		AppsRaw: map[string]json.RawMessage{
			"http": caddyconfig.JSON(httpApp, nil),
		},
	}

	err = caddy.Run(cfg)
	if err != nil {
		return caddy.ExitCodeFailedStartup, err
	}

	fmt.Printf("Caddy 2 proxying from %s to %s\n", fromURL, toURL)

	select {}
}
<|endoftext|>


<commit_before>package libcentrifugo

import (
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/centrifugal/centrifugo/libcentrifugo/logger"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

const (
	VERSION = "0.0.1"
)

var configFile string

func setupLogging() {
	logLevel, ok := logger.LevelMatches[strings.ToUpper(viper.GetString("log_level"))]
	if !ok {
		logLevel = logger.LevelInfo
	}
	logger.SetLogThreshold(logLevel)
	logger.SetStdoutThreshold(logLevel)

	if viper.IsSet("log_file") && viper.GetString("log_file") != "" {
		logger.SetLogFile(viper.GetString("log_file"))
		// do not log into stdout when log file provided
		logger.SetStdoutThreshold(logger.LevelNone)
	}
}

func handleSignals(app *application) {
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGHUP)
	for {
		sig := <-sigc
		logger.INFO.Println("signal received:", sig)
		switch sig {
		case syscall.SIGHUP:
			// reload application configuration on SIGHUP
			// note that you should run checkconfig before reloading configuration
			// as Viper exits when encounters parsing errors
			logger.INFO.Println("reloading configuration")
			err := viper.ReadInConfig()
			if err != nil {
				logger.CRITICAL.Println("unable to locate config file")
				return
			}
			setupLogging()
			app.initialize()
		}
	}
}

func Main() {

	var port string
	var address string
	var debug bool
	var name string
	var web string
	var engn string
	var logLevel string
	var logFile string
	var insecure bool

	var redisHost string
	var redisPort string
	var redisPassword string
	var redisDB string
	var redisURL string
	var redisAPI bool

	var rootCmd = &cobra.Command{
		Use:   "",
		Short: "Centrifugo",
		Long:  "Centrifuge + GO = Centrifugo – harder, better, faster, stronger",
		Run: func(cmd *cobra.Command, args []string) {

			viper.SetDefault("password", "")
			viper.SetDefault("secret", "")
			viper.RegisterAlias("cookie_secret", "")
			viper.SetDefault("max_channel_length", 255)
			viper.SetDefault("channel_prefix", "centrifugo")
			viper.SetDefault("node_ping_interval", 5)
			viper.SetDefault("expired_connection_close_delay", 10)
			viper.SetDefault("presence_ping_interval", 25)
			viper.SetDefault("presence_expire_interval", 60)
			viper.SetDefault("private_channel_prefix", "$")
			viper.SetDefault("namespace_channel_boundary", ":")
			viper.SetDefault("user_channel_boundary", "#")
			viper.SetDefault("user_channel_separator", ",")
			viper.SetDefault("sockjs_url", "https://cdn.jsdelivr.net/sockjs/1.0/sockjs.min.js")

			viper.SetEnvPrefix("centrifugo")
			viper.BindEnv("engine")
			viper.BindEnv("insecure")
			viper.BindEnv("password")
			viper.BindEnv("secret")

			viper.BindPFlag("port", cmd.Flags().Lookup("port"))
			viper.BindPFlag("address", cmd.Flags().Lookup("address"))
			viper.BindPFlag("debug", cmd.Flags().Lookup("debug"))
			viper.BindPFlag("name", cmd.Flags().Lookup("name"))
			viper.BindPFlag("web", cmd.Flags().Lookup("web"))
			viper.BindPFlag("engine", cmd.Flags().Lookup("engine"))
			viper.BindPFlag("insecure", cmd.Flags().Lookup("insecure"))
			viper.BindPFlag("log_level", cmd.Flags().Lookup("log_level"))
			viper.BindPFlag("log_file", cmd.Flags().Lookup("log_file"))
			viper.BindPFlag("redis_host", cmd.Flags().Lookup("redis_host"))
			viper.BindPFlag("redis_port", cmd.Flags().Lookup("redis_port"))
			viper.BindPFlag("redis_password", cmd.Flags().Lookup("redis_password"))
			viper.BindPFlag("redis_db", cmd.Flags().Lookup("redis_db"))
			viper.BindPFlag("redis_url", cmd.Flags().Lookup("redis_url"))
			viper.BindPFlag("redis_api", cmd.Flags().Lookup("redis_api"))

			err := validateConfig(configFile)
			if err != nil {
				logger.FATAL.Fatalln(err)
			}

			viper.SetConfigFile(configFile)
			err = viper.ReadInConfig()
			if err != nil {
				logger.FATAL.Fatalln("unable to locate config file")
			}
			setupLogging()
			logger.INFO.Println("using config file:", viper.ConfigFileUsed())

			app, err := newApplication()
			if err != nil {
				logger.FATAL.Fatalln(err)
			}
			app.initialize()

			var e engine
			switch viper.GetString("engine") {
			case "memory":
				e = newMemoryEngine(app)
			case "redis":
				e = newRedisEngine(
					app,
					viper.GetString("redis_host"),
					viper.GetString("redis_port"),
					viper.GetString("redis_password"),
					viper.GetString("redis_db"),
					viper.GetString("redis_url"),
					viper.GetBool("redis_api"),
				)
			default:
				logger.FATAL.Fatalln("unknown engine: " + viper.GetString("engine"))
			}

			logger.INFO.Println("engine:", viper.GetString("engine"))
			logger.DEBUG.Printf("%v\n", viper.AllSettings())

			app.setEngine(e)

			err = e.initialize()
			if err != nil {
				logger.FATAL.Fatalln(err)
			}

			app.run()

			go handleSignals(app)

			// register raw Websocket endpoint
			http.Handle("/connection/websocket", app.Logged(http.HandlerFunc(app.rawWebsocketHandler)))

			// register SockJS endpoints
			http.Handle("/connection/", app.Logged(newSockJSHandler(app, viper.GetString("sockjs_url"))))

			// register HTTP API endpoint
			http.Handle("/api/", app.Logged(http.HandlerFunc(app.apiHandler)))

			// register admin web interface API endpoints
			http.Handle("/auth/", app.Logged(http.HandlerFunc(app.authHandler)))
			http.Handle("/info/", app.Logged(app.Authenticated(http.HandlerFunc(app.infoHandler))))
			http.Handle("/action/", app.Logged(app.Authenticated(http.HandlerFunc(app.actionHandler))))
			http.Handle("/socket", app.Logged(http.HandlerFunc(app.adminWebsocketHandler)))

			// optionally serve admin web interface application
			webDir := viper.GetString("web")
			if webDir != "" {
				http.Handle("/", http.FileServer(http.Dir(webDir)))
			}

			addr := viper.GetString("address") + ":" + viper.GetString("port")
			logger.INFO.Printf("start serving on %s\n", addr)
			if err := http.ListenAndServe(addr, nil); err != nil {
				logger.FATAL.Fatalln("ListenAndServe:", err)
			}
		},
	}
	rootCmd.Flags().StringVarP(&port, "port", "p", "8000", "port to bind to")
	rootCmd.Flags().StringVarP(&address, "address", "a", "127.0.0.1", "address to listen on")
	rootCmd.Flags().BoolVarP(&debug, "debug", "d", false, "debug mode - please, do not use it in production")
	rootCmd.Flags().StringVarP(&configFile, "config", "c", "config.json", "path to config file")
	rootCmd.Flags().StringVarP(&name, "name", "n", "", "unique node name")
	rootCmd.Flags().StringVarP(&web, "web", "w", "", "optional path to web interface application")
	rootCmd.Flags().StringVarP(&engn, "engine", "e", "memory", "engine to use: memory or redis")
	rootCmd.Flags().BoolVarP(&insecure, "insecure", "", false, "start in insecure mode")
	rootCmd.Flags().StringVarP(&logLevel, "log_level", "", "info", "set the log level: debug, info, error, critical, fatal or none")
	rootCmd.Flags().StringVarP(&logFile, "log_file", "", "", "optional log file - if not specified all logs go to STDOUT")
	rootCmd.Flags().StringVarP(&redisHost, "redis_host", "", "127.0.0.1", "redis host (Redis engine)")
	rootCmd.Flags().StringVarP(&redisPort, "redis_port", "", "6379", "redis port (Redis engine)")
	rootCmd.Flags().StringVarP(&redisPassword, "redis_password", "", "", "redis auth password (Redis engine)")
	rootCmd.Flags().StringVarP(&redisDB, "redis_db", "", "0", "redis database (Redis engine)")
	rootCmd.Flags().StringVarP(&redisURL, "redis_url", "", "", "redis connection URL (Redis engine)")
	rootCmd.Flags().BoolVarP(&redisAPI, "redis_api", "", false, "enable Redis API listener (Redis engine)")

	var versionCmd = &cobra.Command{
		Use:   "version",
		Short: "Centrifugo version number",
		Long:  `Print the version number of Centrifugo`,
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("Centrifugo v%s\n", VERSION)
		},
	}

	var checkConfigFile string

	var checkConfigCmd = &cobra.Command{
		Use:   "checkconfig",
		Short: "Check configuration file",
		Long:  `Check Centrifugo configuration file`,
		Run: func(cmd *cobra.Command, args []string) {
			err := validateConfig(checkConfigFile)
			if err != nil {
				logger.FATAL.Fatalln(err)
			}
		},
	}
	checkConfigCmd.Flags().StringVarP(&checkConfigFile, "config", "c", "config.json", "path to config file to check")

	var outputConfigFile string

	var generateConfigCmd = &cobra.Command{
		Use:   "genconfig",
		Short: "Generate simple configuration file to start with",
		Long:  `Generate simple configuration file to start with`,
		Run: func(cmd *cobra.Command, args []string) {
			err := generateConfig(outputConfigFile)
			if err != nil {
				logger.FATAL.Fatalln(err)
			}
		},
	}
	generateConfigCmd.Flags().StringVarP(&outputConfigFile, "config", "c", "config.json", "path to output config file")

	rootCmd.AddCommand(versionCmd)
	rootCmd.AddCommand(checkConfigCmd)
	rootCmd.AddCommand(generateConfigCmd)
	rootCmd.Execute()
}
<commit_msg>empty default address<commit_after>package libcentrifugo

import (
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"github.com/centrifugal/centrifugo/libcentrifugo/logger"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

const (
	VERSION = "0.0.1"
)

var configFile string

func setupLogging() {
	logLevel, ok := logger.LevelMatches[strings.ToUpper(viper.GetString("log_level"))]
	if !ok {
		logLevel = logger.LevelInfo
	}
	logger.SetLogThreshold(logLevel)
	logger.SetStdoutThreshold(logLevel)

	if viper.IsSet("log_file") && viper.GetString("log_file") != "" {
		logger.SetLogFile(viper.GetString("log_file"))
		// do not log into stdout when log file provided
		logger.SetStdoutThreshold(logger.LevelNone)
	}
}

func handleSignals(app *application) {
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGHUP)
	for {
		sig := <-sigc
		logger.INFO.Println("signal received:", sig)
		switch sig {
		case syscall.SIGHUP:
			// reload application configuration on SIGHUP
			// note that you should run checkconfig before reloading configuration
			// as Viper exits when encounters parsing errors
			logger.INFO.Println("reloading configuration")
			err := viper.ReadInConfig()
			if err != nil {
				logger.CRITICAL.Println("unable to locate config file")
				return
			}
			setupLogging()
			app.initialize()
		}
	}
}

func Main() {

	var port string
	var address string
	var debug bool
	var name string
	var web string
	var engn string
	var logLevel string
	var logFile string
	var insecure bool

	var redisHost string
	var redisPort string
	var redisPassword string
	var redisDB string
	var redisURL string
	var redisAPI bool

	var rootCmd = &cobra.Command{
		Use:   "",
		Short: "Centrifugo",
		Long:  "Centrifuge + GO = Centrifugo – harder, better, faster, stronger",
		Run: func(cmd *cobra.Command, args []string) {

			viper.SetDefault("password", "")
			viper.SetDefault("secret", "")
			viper.RegisterAlias("cookie_secret", "")
			viper.SetDefault("max_channel_length", 255)
			viper.SetDefault("channel_prefix", "centrifugo")
			viper.SetDefault("node_ping_interval", 5)
			viper.SetDefault("expired_connection_close_delay", 10)
			viper.SetDefault("presence_ping_interval", 25)
			viper.SetDefault("presence_expire_interval", 60)
			viper.SetDefault("private_channel_prefix", "$")
			viper.SetDefault("namespace_channel_boundary", ":")
			viper.SetDefault("user_channel_boundary", "#")
			viper.SetDefault("user_channel_separator", ",")
			viper.SetDefault("sockjs_url", "https://cdn.jsdelivr.net/sockjs/1.0/sockjs.min.js")

			viper.SetEnvPrefix("centrifugo")
			viper.BindEnv("engine")
			viper.BindEnv("insecure")
			viper.BindEnv("password")
			viper.BindEnv("secret")

			viper.BindPFlag("port", cmd.Flags().Lookup("port"))
			viper.BindPFlag("address", cmd.Flags().Lookup("address"))
			viper.BindPFlag("debug", cmd.Flags().Lookup("debug"))
			viper.BindPFlag("name", cmd.Flags().Lookup("name"))
			viper.BindPFlag("web", cmd.Flags().Lookup("web"))
			viper.BindPFlag("engine", cmd.Flags().Lookup("engine"))
			viper.BindPFlag("insecure", cmd.Flags().Lookup("insecure"))
			viper.BindPFlag("log_level", cmd.Flags().Lookup("log_level"))
			viper.BindPFlag("log_file", cmd.Flags().Lookup("log_file"))
			viper.BindPFlag("redis_host", cmd.Flags().Lookup("redis_host"))
			viper.BindPFlag("redis_port", cmd.Flags().Lookup("redis_port"))
			viper.BindPFlag("redis_password", cmd.Flags().Lookup("redis_password"))
			viper.BindPFlag("redis_db", cmd.Flags().Lookup("redis_db"))
			viper.BindPFlag("redis_url", cmd.Flags().Lookup("redis_url"))
			viper.BindPFlag("redis_api", cmd.Flags().Lookup("redis_api"))

			err := validateConfig(configFile)
			if err != nil {
				logger.FATAL.Fatalln(err)
			}

			viper.SetConfigFile(configFile)
			err = viper.ReadInConfig()
			if err != nil {
				logger.FATAL.Fatalln("unable to locate config file")
			}
			setupLogging()
			logger.INFO.Println("using config file:", viper.ConfigFileUsed())

			app, err := newApplication()
			if err != nil {
				logger.FATAL.Fatalln(err)
			}
			app.initialize()

			var e engine
			switch viper.GetString("engine") {
			case "memory":
				e = newMemoryEngine(app)
			case "redis":
				e = newRedisEngine(
					app,
					viper.GetString("redis_host"),
					viper.GetString("redis_port"),
					viper.GetString("redis_password"),
					viper.GetString("redis_db"),
					viper.GetString("redis_url"),
					viper.GetBool("redis_api"),
				)
			default:
				logger.FATAL.Fatalln("unknown engine: " + viper.GetString("engine"))
			}

			logger.INFO.Println("engine:", viper.GetString("engine"))
			logger.DEBUG.Printf("%v\n", viper.AllSettings())

			app.setEngine(e)

			err = e.initialize()
			if err != nil {
				logger.FATAL.Fatalln(err)
			}

			app.run()

			go handleSignals(app)

			// register raw Websocket endpoint
			http.Handle("/connection/websocket", app.Logged(http.HandlerFunc(app.rawWebsocketHandler)))

			// register SockJS endpoints
			http.Handle("/connection/", app.Logged(newSockJSHandler(app, viper.GetString("sockjs_url"))))

			// register HTTP API endpoint
			http.Handle("/api/", app.Logged(http.HandlerFunc(app.apiHandler)))

			// register admin web interface API endpoints
			http.Handle("/auth/", app.Logged(http.HandlerFunc(app.authHandler)))
			http.Handle("/info/", app.Logged(app.Authenticated(http.HandlerFunc(app.infoHandler))))
			http.Handle("/action/", app.Logged(app.Authenticated(http.HandlerFunc(app.actionHandler))))
			http.Handle("/socket", app.Logged(http.HandlerFunc(app.adminWebsocketHandler)))

			// optionally serve admin web interface application
			webDir := viper.GetString("web")
			if webDir != "" {
				http.Handle("/", http.FileServer(http.Dir(webDir)))
			}

			addr := viper.GetString("address") + ":" + viper.GetString("port")
			logger.INFO.Printf("start serving on %s\n", addr)
			if err := http.ListenAndServe(addr, nil); err != nil {
				logger.FATAL.Fatalln("ListenAndServe:", err)
			}
		},
	}
	rootCmd.Flags().StringVarP(&port, "port", "p", "8000", "port to bind to")
	rootCmd.Flags().StringVarP(&address, "address", "a", "", "address to listen on")
	rootCmd.Flags().BoolVarP(&debug, "debug", "d", false, "debug mode - please, do not use it in production")
	rootCmd.Flags().StringVarP(&configFile, "config", "c", "config.json", "path to config file")
	rootCmd.Flags().StringVarP(&name, "name", "n", "", "unique node name")
	rootCmd.Flags().StringVarP(&web, "web", "w", "", "optional path to web interface application")
	rootCmd.Flags().StringVarP(&engn, "engine", "e", "memory", "engine to use: memory or redis")
	rootCmd.Flags().BoolVarP(&insecure, "insecure", "", false, "start in insecure mode")
	rootCmd.Flags().StringVarP(&logLevel, "log_level", "", "info", "set the log level: debug, info, error, critical, fatal or none")
	rootCmd.Flags().StringVarP(&logFile, "log_file", "", "", "optional log file - if not specified all logs go to STDOUT")
	rootCmd.Flags().StringVarP(&redisHost, "redis_host", "", "127.0.0.1", "redis host (Redis engine)")
	rootCmd.Flags().StringVarP(&redisPort, "redis_port", "", "6379", "redis port (Redis engine)")
	rootCmd.Flags().StringVarP(&redisPassword, "redis_password", "", "", "redis auth password (Redis engine)")
	rootCmd.Flags().StringVarP(&redisDB, "redis_db", "", "0", "redis database (Redis engine)")
	rootCmd.Flags().StringVarP(&redisURL, "redis_url", "", "", "redis connection URL (Redis engine)")
	rootCmd.Flags().BoolVarP(&redisAPI, "redis_api", "", false, "enable Redis API listener (Redis engine)")

	var versionCmd = &cobra.Command{
		Use:   "version",
		Short: "Centrifugo version number",
		Long:  `Print the version number of Centrifugo`,
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("Centrifugo v%s\n", VERSION)
		},
	}

	var checkConfigFile string

	var checkConfigCmd = &cobra.Command{
		Use:   "checkconfig",
		Short: "Check configuration file",
		Long:  `Check Centrifugo configuration file`,
		Run: func(cmd *cobra.Command, args []string) {
			err := validateConfig(checkConfigFile)
			if err != nil {
				logger.FATAL.Fatalln(err)
			}
		},
	}
	checkConfigCmd.Flags().StringVarP(&checkConfigFile, "config", "c", "config.json", "path to config file to check")

	var outputConfigFile string

	var generateConfigCmd = &cobra.Command{
		Use:   "genconfig",
		Short: "Generate simple configuration file to start with",
		Long:  `Generate simple configuration file to start with`,
		Run: func(cmd *cobra.Command, args []string) {
			err := generateConfig(outputConfigFile)
			if err != nil {
				logger.FATAL.Fatalln(err)
			}
		},
	}
	generateConfigCmd.Flags().StringVarP(&outputConfigFile, "config", "c", "config.json", "path to output config file")

	rootCmd.AddCommand(versionCmd)
	rootCmd.AddCommand(checkConfigCmd)
	rootCmd.AddCommand(generateConfigCmd)
	rootCmd.Execute()
}
<|endoftext|>