<commit_before>package main

import (
	"bytes"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"path"
	"strings"

	"github.com/lightningnetwork/lnd/lncfg"
	"github.com/lightningnetwork/lnd/lnrpc"
	"github.com/lightningnetwork/lnd/walletunlocker"
	"github.com/urfave/cli"
	"gopkg.in/macaroon.v2"
)

var (
	errNoProfileFile = errors.New("no profile file found")
)

// profileEntry is a struct that represents all settings for one specific
// profile.
type profileEntry struct {
	Name        string       `json:"name"`
	RPCServer   string       `json:"rpcserver"`
	LndDir      string       `json:"lnddir"`
	Chain       string       `json:"chain"`
	Network     string       `json:"network"`
	NoMacaroons bool         `json:"no-macaroons,omitempty"`
	TLSCert     string       `json:"tlscert"`
	Macaroons   *macaroonJar `json:"macaroons"`
}

// cert returns the profile's TLS certificate as a x509 certificate pool.
func (e *profileEntry) cert() (*x509.CertPool, error) {
	if e.TLSCert == "" {
		return nil, nil
	}

	cp := x509.NewCertPool()
	if !cp.AppendCertsFromPEM([]byte(e.TLSCert)) {
		return nil, fmt.Errorf("credentials: failed to append " +
			"certificate")
	}
	return cp, nil
}

// getGlobalOptions returns the global connection options. If a profile file
// exists, these global options might be read from a predefined profile. If no
// profile exists, the global options from the command line are returned as an
// ephemeral profile entry.
func getGlobalOptions(ctx *cli.Context, skipMacaroons bool) (*profileEntry,
	error) {

	var profileName string

	// Try to load the default profile file and depending on its existence
	// what profile to use.
	f, err := loadProfileFile(defaultProfileFile)
	switch {
	// The legacy case where no profile file exists and the user also didn't
	// request to use one. We only consider the global options here.
	case err == errNoProfileFile && !ctx.GlobalIsSet("profile"):
		return profileFromContext(ctx, false, skipMacaroons)

	// The file doesn't exist but the user specified an explicit profile.
	case err == errNoProfileFile && ctx.GlobalIsSet("profile"):
		return nil, fmt.Errorf("profile file %s does not exist",
			defaultProfileFile)

	// There is a file but we couldn't read/parse it.
	case err != nil:
		return nil, fmt.Errorf("could not read profile file %s: "+
			"%v", defaultProfileFile, err)

	// The user explicitly disabled the use of profiles for this command by
	// setting the flag to an empty string. We fall back to the default/old
	// behavior.
	case ctx.GlobalIsSet("profile") && ctx.GlobalString("profile") == "":
		return profileFromContext(ctx, false, skipMacaroons)

	// There is a file, but no default profile is specified. The user also
	// didn't specify a profile to use so we fall back to the default/old
	// behavior.
	case !ctx.GlobalIsSet("profile") && len(f.Default) == 0:
		return profileFromContext(ctx, false, skipMacaroons)

	// The user didn't specify a profile but there is a default one defined.
	case !ctx.GlobalIsSet("profile") && len(f.Default) > 0:
		profileName = f.Default

	// The user specified a specific profile to use.
	case ctx.GlobalIsSet("profile"):
		profileName = ctx.GlobalString("profile")
	}

	// If we got to here, we do have a profile file and know the name of the
	// profile to use. Now we just need to make sure it does exist.
	for _, prof := range f.Profiles {
		if prof.Name == profileName {
			return prof, nil
		}
	}

	return nil, fmt.Errorf("profile '%s' not found in file %s", profileName,
		defaultProfileFile)
}

// profileFromContext creates an ephemeral profile entry from the global options
// set in the CLI context.
func profileFromContext(ctx *cli.Context, store, skipMacaroons bool) (
	*profileEntry, error) {

	// Parse the paths of the cert and macaroon. This will validate the
	// chain and network value as well.
	tlsCertPath, macPath, err := extractPathArgs(ctx)
	if err != nil {
		return nil, err
	}

	// Load the certificate file now, if specified. We store it as plain PEM
	// directly.
	var tlsCert []byte
	if lnrpc.FileExists(tlsCertPath) {
		var err error
		tlsCert, err = ioutil.ReadFile(tlsCertPath)
		if err != nil {
			return nil, fmt.Errorf("could not load TLS cert file "+
				"%s: %v", tlsCertPath, err)
		}
	}

	entry := &profileEntry{
		RPCServer:   ctx.GlobalString("rpcserver"),
		LndDir:      lncfg.CleanAndExpandPath(ctx.GlobalString("lnddir")),
		Chain:       ctx.GlobalString("chain"),
		Network:     ctx.GlobalString("network"),
		NoMacaroons: ctx.GlobalBool("no-macaroons"),
		TLSCert:     string(tlsCert),
	}

	// If we aren't using macaroons in general (flag --no-macaroons) or
	// don't need macaroons for this command (wallet unlocker), we can now
	// return already.
	if skipMacaroons || ctx.GlobalBool("no-macaroons") {
		return entry, nil
	}

	// Now load and possibly encrypt the macaroon file.
	macBytes, err := ioutil.ReadFile(macPath)
	if err != nil {
		return nil, fmt.Errorf("unable to read macaroon path (check "+
			"the network setting!): %v", err)
	}
	mac := &macaroon.Macaroon{}
	if err = mac.UnmarshalBinary(macBytes); err != nil {
		return nil, fmt.Errorf("unable to decode macaroon: %v", err)
	}

	var pw []byte
	if store {
		// Read a password from the terminal. If it's empty, we won't
		// encrypt the macaroon and store it plaintext.
		pw, err = capturePassword(
			"Enter password to encrypt macaroon with or leave "+
				"blank to store in plaintext: ", true,
			walletunlocker.ValidatePassword,
		)
		if err != nil {
			return nil, fmt.Errorf("unable to get encryption "+
				"password: %v", err)
		}
	}
	macEntry := &macaroonEntry{}
	if err = macEntry.storeMacaroon(mac, pw); err != nil {
		return nil, fmt.Errorf("unable to store macaroon: %v", err)
	}

	// We determine the name of the macaroon from the file itself but cut
	// off the ".macaroon" at the end.
	macEntry.Name = path.Base(macPath)
	if path.Ext(macEntry.Name) == "macaroon" {
		macEntry.Name = strings.TrimSuffix(macEntry.Name, ".macaroon")
	}

	// Now that we have the macaroon jar as well, let's return the entry
	// with all the values populated.
	entry.Macaroons = &macaroonJar{
		Default: macEntry.Name,
		Timeout: ctx.GlobalInt64("macaroontimeout"),
		IP:      ctx.GlobalString("macaroonip"),
		Jar:     []*macaroonEntry{macEntry},
	}

	return entry, nil
}

// loadProfileFile tries to load the file specified and JSON deserialize it into
// the profile file struct.
func loadProfileFile(file string) (*profileFile, error) {
	if !lnrpc.FileExists(file) {
		return nil, errNoProfileFile
	}

	content, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, fmt.Errorf("could not load profile file %s: %v",
			file, err)
	}
	f := &profileFile{}
	err = f.unmarshalJSON(content)
	if err != nil {
		return nil, fmt.Errorf("could not unmarshal profile file %s: "+
			"%v", file, err)
	}
	return f, nil
}

// saveProfileFile stores the given profile file struct in the specified file,
// overwriting it if it already existed.
func saveProfileFile(file string, f *profileFile) error {
	content, err := f.marshalJSON()
	if err != nil {
		return fmt.Errorf("could not marshal profile: %v", err)
	}
	return ioutil.WriteFile(file, content, 0644)
}

// profileFile is a struct that represents the whole content of a profile file.
type profileFile struct {
	Default  string          `json:"default,omitempty"`
	Profiles []*profileEntry `json:"profiles"`
}

// unmarshalJSON tries to parse the given JSON and unmarshal it into the
// receiving instance.
func (f *profileFile) unmarshalJSON(content []byte) error {
	return json.Unmarshal(content, f)
}

// marshalJSON serializes the receiving instance to formatted/indented JSON.
func (f *profileFile) marshalJSON() ([]byte, error) {
	b, err := json.Marshal(f)
	if err != nil {
		return nil, fmt.Errorf("error JSON marshalling profile: %v",
			err)
	}

	var out bytes.Buffer
	err = json.Indent(&out, b, "", "  ")
	if err != nil {
		return nil, fmt.Errorf("error indenting profile JSON: %v", err)
	}
	out.WriteString("\n")
	return out.Bytes(), nil
}
<commit_msg>lncli: check for existence of tls file<commit_after>package main

import (
	"bytes"
	"crypto/x509"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"path"
	"strings"

	"github.com/lightningnetwork/lnd/lncfg"
	"github.com/lightningnetwork/lnd/lnrpc"
	"github.com/lightningnetwork/lnd/walletunlocker"
	"github.com/urfave/cli"
	"gopkg.in/macaroon.v2"
)

var (
	errNoProfileFile = errors.New("no profile file found")
)

// profileEntry is a struct that represents all settings for one specific
// profile.
type profileEntry struct {
	Name        string       `json:"name"`
	RPCServer   string       `json:"rpcserver"`
	LndDir      string       `json:"lnddir"`
	Chain       string       `json:"chain"`
	Network     string       `json:"network"`
	NoMacaroons bool         `json:"no-macaroons,omitempty"`
	TLSCert     string       `json:"tlscert"`
	Macaroons   *macaroonJar `json:"macaroons"`
}

// cert returns the profile's TLS certificate as a x509 certificate pool.
func (e *profileEntry) cert() (*x509.CertPool, error) {
	if e.TLSCert == "" {
		return nil, nil
	}

	cp := x509.NewCertPool()
	if !cp.AppendCertsFromPEM([]byte(e.TLSCert)) {
		return nil, fmt.Errorf("credentials: failed to append " +
			"certificate")
	}
	return cp, nil
}

// getGlobalOptions returns the global connection options. If a profile file
// exists, these global options might be read from a predefined profile. If no
// profile exists, the global options from the command line are returned as an
// ephemeral profile entry.
func getGlobalOptions(ctx *cli.Context, skipMacaroons bool) (*profileEntry,
	error) {

	var profileName string

	// Try to load the default profile file and depending on its existence
	// what profile to use.
	f, err := loadProfileFile(defaultProfileFile)
	switch {
	// The legacy case where no profile file exists and the user also didn't
	// request to use one. We only consider the global options here.
	case err == errNoProfileFile && !ctx.GlobalIsSet("profile"):
		return profileFromContext(ctx, false, skipMacaroons)

	// The file doesn't exist but the user specified an explicit profile.
	case err == errNoProfileFile && ctx.GlobalIsSet("profile"):
		return nil, fmt.Errorf("profile file %s does not exist",
			defaultProfileFile)

	// There is a file but we couldn't read/parse it.
	case err != nil:
		return nil, fmt.Errorf("could not read profile file %s: "+
			"%v", defaultProfileFile, err)

	// The user explicitly disabled the use of profiles for this command by
	// setting the flag to an empty string. We fall back to the default/old
	// behavior.
	case ctx.GlobalIsSet("profile") && ctx.GlobalString("profile") == "":
		return profileFromContext(ctx, false, skipMacaroons)

	// There is a file, but no default profile is specified. The user also
	// didn't specify a profile to use so we fall back to the default/old
	// behavior.
	case !ctx.GlobalIsSet("profile") && len(f.Default) == 0:
		return profileFromContext(ctx, false, skipMacaroons)

	// The user didn't specify a profile but there is a default one defined.
	case !ctx.GlobalIsSet("profile") && len(f.Default) > 0:
		profileName = f.Default

	// The user specified a specific profile to use.
	case ctx.GlobalIsSet("profile"):
		profileName = ctx.GlobalString("profile")
	}

	// If we got to here, we do have a profile file and know the name of the
	// profile to use. Now we just need to make sure it does exist.
	for _, prof := range f.Profiles {
		if prof.Name == profileName {
			return prof, nil
		}
	}

	return nil, fmt.Errorf("profile '%s' not found in file %s", profileName,
		defaultProfileFile)
}

// profileFromContext creates an ephemeral profile entry from the global options
// set in the CLI context.
func profileFromContext(ctx *cli.Context, store, skipMacaroons bool) (
	*profileEntry, error) {

	// Parse the paths of the cert and macaroon. This will validate the
	// chain and network value as well.
	tlsCertPath, macPath, err := extractPathArgs(ctx)
	if err != nil {
		return nil, err
	}

	// Load the certificate file now, if specified. We store it as plain PEM
	// directly.
	var tlsCert []byte
	if tlsCertPath != "" {
		var err error
		tlsCert, err = ioutil.ReadFile(tlsCertPath)
		if err != nil {
			return nil, fmt.Errorf("could not load TLS cert file: %v", err)
		}
	}

	entry := &profileEntry{
		RPCServer:   ctx.GlobalString("rpcserver"),
		LndDir:      lncfg.CleanAndExpandPath(ctx.GlobalString("lnddir")),
		Chain:       ctx.GlobalString("chain"),
		Network:     ctx.GlobalString("network"),
		NoMacaroons: ctx.GlobalBool("no-macaroons"),
		TLSCert:     string(tlsCert),
	}

	// If we aren't using macaroons in general (flag --no-macaroons) or
	// don't need macaroons for this command (wallet unlocker), we can now
	// return already.
	if skipMacaroons || ctx.GlobalBool("no-macaroons") {
		return entry, nil
	}

	// Now load and possibly encrypt the macaroon file.
	macBytes, err := ioutil.ReadFile(macPath)
	if err != nil {
		return nil, fmt.Errorf("unable to read macaroon path (check "+
			"the network setting!): %v", err)
	}
	mac := &macaroon.Macaroon{}
	if err = mac.UnmarshalBinary(macBytes); err != nil {
		return nil, fmt.Errorf("unable to decode macaroon: %v", err)
	}

	var pw []byte
	if store {
		// Read a password from the terminal. If it's empty, we won't
		// encrypt the macaroon and store it plaintext.
		pw, err = capturePassword(
			"Enter password to encrypt macaroon with or leave "+
				"blank to store in plaintext: ", true,
			walletunlocker.ValidatePassword,
		)
		if err != nil {
			return nil, fmt.Errorf("unable to get encryption "+
				"password: %v", err)
		}
	}
	macEntry := &macaroonEntry{}
	if err = macEntry.storeMacaroon(mac, pw); err != nil {
		return nil, fmt.Errorf("unable to store macaroon: %v", err)
	}

	// We determine the name of the macaroon from the file itself but cut
	// off the ".macaroon" at the end.
	macEntry.Name = path.Base(macPath)
	if path.Ext(macEntry.Name) == "macaroon" {
		macEntry.Name = strings.TrimSuffix(macEntry.Name, ".macaroon")
	}

	// Now that we have the macaroon jar as well, let's return the entry
	// with all the values populated.
	entry.Macaroons = &macaroonJar{
		Default: macEntry.Name,
		Timeout: ctx.GlobalInt64("macaroontimeout"),
		IP:      ctx.GlobalString("macaroonip"),
		Jar:     []*macaroonEntry{macEntry},
	}

	return entry, nil
}

// loadProfileFile tries to load the file specified and JSON deserialize it into
// the profile file struct.
func loadProfileFile(file string) (*profileFile, error) {
	if !lnrpc.FileExists(file) {
		return nil, errNoProfileFile
	}

	content, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, fmt.Errorf("could not load profile file %s: %v",
			file, err)
	}
	f := &profileFile{}
	err = f.unmarshalJSON(content)
	if err != nil {
		return nil, fmt.Errorf("could not unmarshal profile file %s: "+
			"%v", file, err)
	}
	return f, nil
}

// saveProfileFile stores the given profile file struct in the specified file,
// overwriting it if it already existed.
func saveProfileFile(file string, f *profileFile) error {
	content, err := f.marshalJSON()
	if err != nil {
		return fmt.Errorf("could not marshal profile: %v", err)
	}
	return ioutil.WriteFile(file, content, 0644)
}

// profileFile is a struct that represents the whole content of a profile file.
type profileFile struct {
	Default  string          `json:"default,omitempty"`
	Profiles []*profileEntry `json:"profiles"`
}

// unmarshalJSON tries to parse the given JSON and unmarshal it into the
// receiving instance.
func (f *profileFile) unmarshalJSON(content []byte) error {
	return json.Unmarshal(content, f)
}

// marshalJSON serializes the receiving instance to formatted/indented JSON.
func (f *profileFile) marshalJSON() ([]byte, error) {
	b, err := json.Marshal(f)
	if err != nil {
		return nil, fmt.Errorf("error JSON marshalling profile: %v",
			err)
	}

	var out bytes.Buffer
	err = json.Indent(&out, b, "", "  ")
	if err != nil {
		return nil, fmt.Errorf("error indenting profile JSON: %v", err)
	}
	out.WriteString("\n")
	return out.Bytes(), nil
}
<|endoftext|>


<commit_before>package main

import (
    "os"
    "github.com/urfave/cli"
    vault "github.com/hashicorp/vault/api"
    "github.com/nebtex/menshend/pkg/pfclient"
    mconfig "github.com/nebtex/menshend/pkg/config"
    "fmt"
)

func main() {
    app := cli.NewApp()

    app.Commands = []cli.Command{
        {
            Action: func(c *cli.Context) error {
                fl := &pfFlags{}
                fl.token = c.String("token")
                fl.keepAlive = c.Duration("keepalive")
                fl.port = c.String("port")
                fl.verbose = c.Bool("verbose")
                fl.server = c.String("server")
                return portForward(fl, pfclient.PFConnect)
            },
            Name:    "port-forward",
            Flags: []cli.Flag{
                cli.StringFlag{
                    Name: "server, s",
                    Value: "",
                    Usage: "full http(s) url of the service under the Menshend space wanted to be tunneled, ip addresses are not allowed",
                },
                cli.StringFlag{
                    Name: "port, p",
                    Value: "",
                    Usage: "[local-host]:[local-port]:<remote-port>",
                },
                cli.StringFlag{
                    Name: "token, t",
                    Value: "",
                    Usage: "vault token",
                    EnvVar: vault.EnvVaultToken,
                },
                cli.DurationFlag{
                    Name: "keepalive, k",
                    Usage: "An optional keepalive interval. Since the underlying" +
                        "transport is HTTP, in many instances we'll be traversing through" +
                        "proxies, often these proxies will close idle connections. You must" +
                        "specify a time with a unit, for example '30s' or '2m'. Defaults" +
                        "to '0s' (disabled)",
                },
                cli.BoolFlag{
                    Name: "verbose, v",
                    Usage: "verbose debug",
                },
            },
            Usage:   `this command is based on the chisel project https://github.com/jpillora/chisel

	● Examples:

	tunneling a mongo database, locate in the machine learning lab of the example.com company to the localhost

	  ♦ make mongo available on localhost:27017
	    menshend port-forward   --server https://mongo.ml-lab.example.com  --port 27017
    ♦ ... mongo ... localhost:3000
        menshend port-forward	--server https://mongo.ml-lab.example.com  --port 3000
    ♦ ... mongo ... 192.168.0.5:3000
        menshend port-forward	--server https://labs.example.com  --port 192.168.0.5:3000`,
        },
        {
            Name:    "admin",
            Aliases: []string{"adminServices"},
            Usage:   "admin api - add/update/delete services",
            Subcommands: []cli.Command{
                {
                    Name:  "get",
                    Aliases:[]string{"read"},
                    Usage: "return service definition",
                    Flags: apiClientGetFlags(),
                    Action: adminCMDHandler("get"),
                },
                {
                    Name:  "delete",
                    Aliases:[]string{"remove", "del", "eliminate", "erase"},
                    Usage: "delete a service",
                    Flags: apiClientGetFlags(),
                    Action: adminCMDHandler("delete"),
                },
                {
                    Name:  "upsert",
                    Aliases:[]string{"save", "apply", "update", "put", "write", "upload", "add", "replace", "create", "post"},
                    Usage: "create or update a service",
                    Flags: apiClientGetFlags(),
                    Action: adminCMDHandler("upsert"),
                },
            },
        },
        {
            Name:    "server",
            Aliases: []string{"run", "start"},
            Usage:   "run menshend server",
            Flags: []cli.Flag{
                cli.StringFlag{
                    Name: "port, p",
                    Value: "8787",
                    Usage: "bind port",
                },
                cli.StringFlag{
                    Name: "config, c",
                    Value: "",
                    Usage: "config file",
                },
                cli.StringFlag{
                    Name: "address, a",
                    Value: "0.0.0.0",
                    Usage: "bind address",
                },
            },
            Action: func(c *cli.Context) error {
                config := c.String("config")
                mconfig.ConfigFile = &config
                err := mconfig.LoadConfig()
                if err != nil {
                    return err
                }
                return menshendServer(c.String("address"), c.String("port"))
            },
        },
        {
            Name:    "version",
            Aliases: []string{"release"},
            Usage:   "get binary version",
            Action: func(c *cli.Context) error {
                fmt.Println(Version)
                return nil
            },
        },
    }

    app.Run(os.Args)
}
<commit_msg>update cli description<commit_after>package main

import (
    "os"
    "github.com/urfave/cli"
    vault "github.com/hashicorp/vault/api"
    "github.com/nebtex/menshend/pkg/pfclient"
    mconfig "github.com/nebtex/menshend/pkg/config"
    "fmt"
)

func main() {
    app := cli.NewApp()
    app.Name = "menshend"
    app.Usage = ""
    app.Version = Version
    app.Authors = []cli.Author{{"nebtex", "publicdev@nebtex.com"}}

    app.Commands = []cli.Command{
        {
            Action: func(c *cli.Context) error {
                fl := &pfFlags{}
                fl.token = c.String("token")
                fl.keepAlive = c.Duration("keepalive")
                fl.port = c.String("port")
                fl.verbose = c.Bool("verbose")
                fl.server = c.String("server")
                return portForward(fl, pfclient.PFConnect)
            },
            Name:    "port-forward",
            Flags: []cli.Flag{
                cli.StringFlag{
                    Name: "server, s",
                    Value: "",
                    Usage: "full http(s) url of the service under the Menshend space wanted to be tunneled, ip addresses are not allowed",
                },
                cli.StringFlag{
                    Name: "port, p",
                    Value: "",
                    Usage: "[local-host]:<local-port>",
                },
                cli.StringFlag{
                    Name: "token, t",
                    Value: "",
                    Usage: "vault token",
                    EnvVar: vault.EnvVaultToken,
                },
                cli.DurationFlag{
                    Name: "keepalive, k",
                    Usage: "An optional keepalive interval. Since the underlying \n transport is HTTP, in many instances we'll be traversing through " +
                        "proxies, often these proxies will close idle connections. \n You must" +
                        "specify a time with a unit, for example '30s' or '2m'. Defaults" +
                        "to '0s' (disabled)",
                },
                cli.BoolFlag{
                    Name: "verbose, v",
                    Usage: "verbose debug",
                },
            },
            Usage:  "Create secure tunnels",
            Description:`this command is adapted from the chisel project https://github.com/jpillora/chisel

 ● Examples:

 tunneling a mongo database, locate in some of the laboratories of the example.com organization to the localhost

 ♦ make mongo available on localhost:27017
     menshend port-forward   --server https://mongo.ml-lab.example.com  --port 27017
 ♦ ... mongo ... localhost:3000
     menshend port-forward	--server https://mongo.ml-lab.example.com  --port 3000
 ♦ ... mongo ... 192.168.0.5:3000
     menshend port-forward	--server https://labs.example.com  --port 192.168.0.5:3000`,
        },
        {
            Name:    "admin",
            Aliases: []string{"adminServices"},
            Usage:   "admin api - add/update/delete services",
            Subcommands: []cli.Command{
                {
                    Name:  "get",
                    Aliases:[]string{"read"},
                    Usage: "return service definition",
                    Flags: apiClientGetFlags(),
                    Action: adminCMDHandler("get"),
                },
                {
                    Name:  "delete",
                    Aliases:[]string{"remove", "del", "eliminate", "erase"},
                    Usage: "delete a service",
                    Flags: apiClientGetFlags(),
                    Action: adminCMDHandler("delete"),
                },
                {
                    Name:  "upsert",
                    Aliases:[]string{"save", "apply", "update", "put", "write", "upload", "add", "replace", "create", "post"},
                    Usage: "create or update a service",
                    Flags: apiClientGetFlags(),
                    Action: adminCMDHandler("upsert"),
                },
            },
        },
        {
            Name:    "server",
            Aliases: []string{"run", "start"},
            Usage:   "run menshend server",
            Flags: []cli.Flag{
                cli.StringFlag{
                    Name: "port, p",
                    Value: "8787",
                    Usage: "bind port",
                },
                cli.StringFlag{
                    Name: "config, c",
                    Value: "",
                    Usage: "config file",
                },
                cli.StringFlag{
                    Name: "address, a",
                    Value: "0.0.0.0",
                    Usage: "bind address",
                },
            },
            Action: func(c *cli.Context) error {
                config := c.String("config")
                mconfig.ConfigFile = &config
                err := mconfig.LoadConfig()
                if err != nil {
                    return err
                }
                return menshendServer(c.String("address"), c.String("port"))
            },
        },
        {
            Name:    "version",
            Aliases: []string{"release"},
            Usage:   "get binary version",
            Action: func(c *cli.Context) error {
                fmt.Println(Version)
                return nil
            },
        },
    }

    app.Run(os.Args)
}
<|endoftext|>


<commit_before>package couriermodel

import (
	"database/sql"
	"fmt"
	"github.com/hackform/governor"
	"github.com/hackform/governor/service/db"
	"github.com/hackform/governor/util/uid"
	"net/http"
	"strings"
	"time"
)

const (
	uidRandSize   = 8
	linkTableName = "courierlinks"
	moduleID      = "couriermodel"
	moduleIDLink  = moduleID + ".Link"
)

type (
	// Repo is a courier repository
	Repo interface {
		NewLink(linkid, url, creatorid string) (*LinkModel, *governor.Error)
		NewLinkAuto(url, creatorid string) (*LinkModel, *governor.Error)
		//GetLinkByID(id string) (*LinkModel, *governor.Error)
		//InsertLink(m *LinkModel) *governor.Error
		//DeleteLink(m *LinkModel) *governor.Error
		Setup() *governor.Error
	}

	repo struct {
		db *sql.DB
	}

	// LinkModel is the db link model
	LinkModel struct {
		LinkID       string `json:"linkid"`
		URL          string `json:"url"`
		CreatorID    string `json:"creatorid"`
		CreationTime int64  `json:"creation_time"`
	}
)

// New creates a new courier repo
func New(config governor.Config, l governor.Logger, d db.Database) Repo {
	l.Info("initialized courier repo", moduleID, "initialize courier repo", 0, nil)
	return &repo{
		db: d.DB(),
	}
}

const (
	moduleIDLinkNew = moduleIDLink + ".New"
)

// NewLink creates a new link model
func (r *repo) NewLink(linkid, url, creatorid string) (*LinkModel, *governor.Error) {
	return &LinkModel{
		LinkID:       linkid,
		URL:          url,
		CreatorID:    creatorid,
		CreationTime: time.Now().Unix(),
	}, nil
}

// NewLinkAuto creates a new courier model with the link id randomly generated
func (r *repo) NewLinkAuto(url, creatorid string) (*LinkModel, *governor.Error) {
	mUID, err := uid.NewU(0, uidRandSize)
	if err != nil {
		err.AddTrace(moduleIDLinkNew)
		return nil, err
	}
	rawb64 := strings.TrimRight(mUID.Base64(), "=")
	return r.NewLink(rawb64, url, creatorid)
}

const (
	moduleIDSetup = moduleID + ".Setup"
)

var (
	sqlLinksSetup = fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (linkid VARCHAR(64) PRIMARY KEY, url VARCHAR(2048) NOT NULL, creatorid VARCHAR(64), creation_time BIGINT NOT NULL);", linkTableName)
)

// Setup creates new Courier tables
func (r *repo) Setup() *governor.Error {
	_, err := r.db.Exec(sqlLinksSetup)
	if err != nil {
		return governor.NewError(moduleIDSetup, err.Error(), 0, http.StatusInternalServerError)
	}
	return nil
}
<commit_msg>Add crud methods to courier link repo<commit_after>package couriermodel

import (
	"database/sql"
	"fmt"
	"github.com/hackform/governor"
	"github.com/hackform/governor/service/db"
	"github.com/hackform/governor/util/uid"
	"github.com/lib/pq"
	"net/http"
	"strings"
	"time"
)

const (
	uidRandSize   = 8
	linkTableName = "courierlinks"
	moduleID      = "couriermodel"
	moduleIDLink  = moduleID + ".Link"
)

type (
	// Repo is a courier repository
	Repo interface {
		NewLink(linkid, url, creatorid string) (*LinkModel, *governor.Error)
		NewLinkAuto(url, creatorid string) (*LinkModel, *governor.Error)
		GetLink(linkid string) (*LinkModel, *governor.Error)
		InsertLink(m *LinkModel) *governor.Error
		UpdateLink(m *LinkModel) *governor.Error
		DeleteLink(m *LinkModel) *governor.Error
		Setup() *governor.Error
	}

	repo struct {
		db *sql.DB
	}

	// LinkModel is the db link model
	LinkModel struct {
		LinkID       string `json:"linkid"`
		URL          string `json:"url"`
		CreatorID    string `json:"creatorid"`
		CreationTime int64  `json:"creation_time"`
	}
)

// New creates a new courier repo
func New(config governor.Config, l governor.Logger, d db.Database) Repo {
	l.Info("initialized courier repo", moduleID, "initialize courier repo", 0, nil)
	return &repo{
		db: d.DB(),
	}
}

const (
	moduleIDLinkNew = moduleIDLink + ".New"
)

// NewLink creates a new link model
func (r *repo) NewLink(linkid, url, creatorid string) (*LinkModel, *governor.Error) {
	return &LinkModel{
		LinkID:       linkid,
		URL:          url,
		CreatorID:    creatorid,
		CreationTime: time.Now().Unix(),
	}, nil
}

// NewLinkAuto creates a new courier model with the link id randomly generated
func (r *repo) NewLinkAuto(url, creatorid string) (*LinkModel, *governor.Error) {
	mUID, err := uid.NewU(0, uidRandSize)
	if err != nil {
		err.AddTrace(moduleIDLinkNew)
		return nil, err
	}
	rawb64 := strings.TrimRight(mUID.Base64(), "=")
	return r.NewLink(rawb64, url, creatorid)
}

const (
	moduleIDLinkGet = moduleIDLink + ".Get"
)

var (
	sqlLinkGet = fmt.Sprintf("SELECT linkid, url, creatorid, creation_time FROM %s WHERE linkid=$1;", linkTableName)
)

// GetLink returns a link model with the given id
func (r *repo) GetLink(linkid string) (*LinkModel, *governor.Error) {
	m := &LinkModel{}
	if err := r.db.QueryRow(sqlLinkGet, linkid).Scan(&m.LinkID, &m.URL, &m.CreatorID, &m.CreationTime); err != nil {
		if err == sql.ErrNoRows {
			return nil, governor.NewError(moduleIDLinkGet, "no link found with that id", 2, http.StatusNotFound)
		}
		return nil, governor.NewError(moduleIDLinkGet, err.Error(), 0, http.StatusInternalServerError)
	}
	return m, nil
}

const (
	moduleIDLinkIns = moduleIDLink + ".Insert"
)

var (
	sqlLinkInsert = fmt.Sprintf("INSERT INTO %s (linkid, url, creatorid, creation_time) VALUES ($1, $2, $3, $4);", linkTableName)
)

// InsertLink inserts the link model into the db
func (r *repo) InsertLink(m *LinkModel) *governor.Error {
	_, err := r.db.Exec(sqlLinkInsert, m.LinkID, m.URL, m.CreatorID, m.CreationTime)
	if err != nil {
		if postgresErr, ok := err.(*pq.Error); ok {
			switch postgresErr.Code {
			case "23505": // unique_violation
				return governor.NewError(moduleIDLinkIns, err.Error(), 3, http.StatusBadRequest)
			default:
				return governor.NewError(moduleIDLinkIns, err.Error(), 0, http.StatusInternalServerError)
			}
		}
	}
	return nil
}

const (
	moduleIDLinkUp = moduleIDLink + ".Update"
)

var (
	sqlLinkUpdate = fmt.Sprintf("UPDATE %s SET (url, creatorid, creation_time) = ($2, $3, $4) WHERE linkid=$1;", linkTableName)
)

// UpdateLink updates the link model in the db
func (r *repo) UpdateLink(m *LinkModel) *governor.Error {
	_, err := r.db.Exec(sqlLinkUpdate, m.LinkID, m.URL, m.CreatorID, m.CreationTime)
	if err != nil {
		return governor.NewError(moduleIDLinkUp, err.Error(), 0, http.StatusInternalServerError)
	}
	return nil
}

const (
	moduleIDLinkDel = moduleIDLink + ".Del"
)

var (
	sqlLinkDelete = fmt.Sprintf("DELETE FROM %s WHERE linkid=$1;", linkTableName)
)

// DeleteLink deletes the link model in the db
func (r *repo) DeleteLink(m *LinkModel) *governor.Error {
	if _, err := r.db.Exec(sqlLinkDelete, m.LinkID); err != nil {
		return governor.NewError(moduleIDLinkDel, err.Error(), 0, http.StatusInternalServerError)
	}
	return nil
}

const (
	moduleIDSetup = moduleID + ".Setup"
)

var (
	sqlLinksSetup = fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (linkid VARCHAR(64) PRIMARY KEY, url VARCHAR(2048) NOT NULL, creatorid VARCHAR(64), creation_time BIGINT NOT NULL);", linkTableName)
)

// Setup creates new Courier tables
func (r *repo) Setup() *governor.Error {
	_, err := r.db.Exec(sqlLinksSetup)
	if err != nil {
		return governor.NewError(moduleIDSetup, err.Error(), 0, http.StatusInternalServerError)
	}
	return nil
}
<|endoftext|>


<commit_before>package job

import (
	"fmt"
)

type Job struct {
	Name            string
	JobRequirements map[string][]string
	Payload         *JobPayload
	State           *JobState
}

func NewJob(name string, requirements map[string][]string, payload *JobPayload, state *JobState) *Job {
	return &Job{name, requirements, payload, state}
}

func (self *Job) String() string {
	return fmt.Sprintf("{ Name=%s }", self.Name)
}

func (self *Job) Requirements() map[string][]string {
	if self.Payload != nil {
		stacked := make(map[string][]string, 0)

		for key, values := range self.Payload.Unit.Requirements() {
			stacked[key] = values
		}

		for key, values := range self.JobRequirements {
			stacked[key] = values
		}

		return stacked

	} else {
		return self.JobRequirements
	}
}
<commit_msg>refactor(job): Remove unnecessary String() method<commit_after>package job

type Job struct {
	Name            string
	JobRequirements map[string][]string
	Payload         *JobPayload
	State           *JobState
}

func NewJob(name string, requirements map[string][]string, payload *JobPayload, state *JobState) *Job {
	return &Job{name, requirements, payload, state}
}

func (self *Job) Requirements() map[string][]string {
	if self.Payload != nil {
		stacked := make(map[string][]string, 0)

		for key, values := range self.Payload.Unit.Requirements() {
			stacked[key] = values
		}

		for key, values := range self.JobRequirements {
			stacked[key] = values
		}

		return stacked

	} else {
		return self.JobRequirements
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package blueprint

import (
	"fmt"
	"github.com/google/blueprint/pathtools"
)

type Singleton interface {
	GenerateBuildActions(SingletonContext)
}

type SingletonContext interface {
	Config() interface{}

	ModuleName(module Module) string
	ModuleDir(module Module) string
	ModuleSubDir(module Module) string
	ModuleType(module Module) string
	BlueprintFile(module Module) string

	ModuleErrorf(module Module, format string, args ...interface{})
	Errorf(format string, args ...interface{})
	Failed() bool

	Variable(pctx PackageContext, name, value string)
	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule
	Build(pctx PackageContext, params BuildParams)
	RequireNinjaVersion(major, minor, micro int)

	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
	// that controls where Ninja stores its build log files.  This value can be
	// set at most one time for a single build, later calls are ignored.
	SetNinjaBuildDir(pctx PackageContext, value string)

	// Eval takes a string with embedded ninja variables, and returns a string
	// with all of the variables recursively expanded. Any variables references
	// are expanded in the scope of the PackageContext.
	Eval(pctx PackageContext, ninjaStr string) (string, error)

	VisitAllModules(visit func(Module))
	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
	VisitDepsDepthFirst(module Module, visit func(Module))
	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
		visit func(Module))

	VisitAllModuleVariants(module Module, visit func(Module))

	PrimaryModule(module Module) Module
	FinalModule(module Module) Module

	AddNinjaFileDeps(deps ...string)

	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
	// builder whenever a file matching the pattern as added or removed, without rerunning if a
	// file that does not match the pattern is added to a searched directory.
	GlobWithDeps(pattern string, excludes []string) ([]string, error)

	Fs() pathtools.FileSystem
}

var _ SingletonContext = (*singletonContext)(nil)

type singletonContext struct {
	context *Context
	config  interface{}
	scope   *localScope
	globals *liveTracker

	ninjaFileDeps []string
	errs          []error

	actionDefs localBuildActions
}

func (s *singletonContext) Config() interface{} {
	return s.config
}

func (s *singletonContext) ModuleName(logicModule Module) string {
	return s.context.ModuleName(logicModule)
}

func (s *singletonContext) ModuleDir(logicModule Module) string {
	return s.context.ModuleDir(logicModule)
}

func (s *singletonContext) ModuleSubDir(logicModule Module) string {
	return s.context.ModuleSubDir(logicModule)
}

func (s *singletonContext) ModuleType(logicModule Module) string {
	return s.context.ModuleType(logicModule)
}

func (s *singletonContext) BlueprintFile(logicModule Module) string {
	return s.context.BlueprintFile(logicModule)
}

func (s *singletonContext) error(err error) {
	if err != nil {
		s.errs = append(s.errs, err)
	}
}

func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
	args ...interface{}) {

	s.error(s.context.ModuleErrorf(logicModule, format, args...))
}

func (s *singletonContext) Errorf(format string, args ...interface{}) {
	// TODO: Make this not result in the error being printed as "internal error"
	s.error(fmt.Errorf(format, args...))
}

func (s *singletonContext) Failed() bool {
	return len(s.errs) > 0
}

func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
	s.scope.ReparentTo(pctx)

	v, err := s.scope.AddLocalVariable(name, value)
	if err != nil {
		panic(err)
	}

	s.actionDefs.variables = append(s.actionDefs.variables, v)
}

func (s *singletonContext) Rule(pctx PackageContext, name string,
	params RuleParams, argNames ...string) Rule {

	s.scope.ReparentTo(pctx)

	r, err := s.scope.AddLocalRule(name, &params, argNames...)
	if err != nil {
		panic(err)
	}

	s.actionDefs.rules = append(s.actionDefs.rules, r)

	return r
}

func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
	s.scope.ReparentTo(pctx)

	def, err := parseBuildParams(s.scope, &params)
	if err != nil {
		panic(err)
	}

	s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
}

func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
	s.scope.ReparentTo(pctx)

	ninjaStr, err := parseNinjaString(s.scope, str)
	if err != nil {
		return "", err
	}

	err = s.globals.addNinjaStringDeps(ninjaStr)
	if err != nil {
		return "", err
	}

	return ninjaStr.Eval(s.globals.variables)
}

func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
	s.context.requireNinjaVersion(major, minor, micro)
}

func (s *singletonContext) SetNinjaBuildDir(pctx PackageContext, value string) {
	s.scope.ReparentTo(pctx)

	ninjaValue, err := parseNinjaString(s.scope, value)
	if err != nil {
		panic(err)
	}

	s.context.setNinjaBuildDir(ninjaValue)
}

func (s *singletonContext) VisitAllModules(visit func(Module)) {
	s.context.VisitAllModules(visit)
}

func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
	visit func(Module)) {

	s.context.VisitAllModulesIf(pred, visit)
}

func (s *singletonContext) VisitDepsDepthFirst(module Module,
	visit func(Module)) {

	s.context.VisitDepsDepthFirst(module, visit)
}

func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
	pred func(Module) bool, visit func(Module)) {

	s.context.VisitDepsDepthFirstIf(module, pred, visit)
}

func (s *singletonContext) PrimaryModule(module Module) Module {
	return s.context.PrimaryModule(module)
}

func (s *singletonContext) FinalModule(module Module) Module {
	return s.context.FinalModule(module)
}

func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
	s.context.VisitAllModuleVariants(module, visit)
}

func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
	s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
}

func (s *singletonContext) GlobWithDeps(pattern string,
	excludes []string) ([]string, error) {
	return s.context.glob(pattern, excludes)
}

func (s *singletonContext) Fs() pathtools.FileSystem {
	return s.context.fs
}
<commit_msg>Fix import list in singleton_ctx.go<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package blueprint

import (
	"fmt"

	"github.com/google/blueprint/pathtools"
)

type Singleton interface {
	GenerateBuildActions(SingletonContext)
}

type SingletonContext interface {
	Config() interface{}

	ModuleName(module Module) string
	ModuleDir(module Module) string
	ModuleSubDir(module Module) string
	ModuleType(module Module) string
	BlueprintFile(module Module) string

	ModuleErrorf(module Module, format string, args ...interface{})
	Errorf(format string, args ...interface{})
	Failed() bool

	Variable(pctx PackageContext, name, value string)
	Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule
	Build(pctx PackageContext, params BuildParams)
	RequireNinjaVersion(major, minor, micro int)

	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
	// that controls where Ninja stores its build log files.  This value can be
	// set at most one time for a single build, later calls are ignored.
	SetNinjaBuildDir(pctx PackageContext, value string)

	// Eval takes a string with embedded ninja variables, and returns a string
	// with all of the variables recursively expanded. Any variables references
	// are expanded in the scope of the PackageContext.
	Eval(pctx PackageContext, ninjaStr string) (string, error)

	VisitAllModules(visit func(Module))
	VisitAllModulesIf(pred func(Module) bool, visit func(Module))
	VisitDepsDepthFirst(module Module, visit func(Module))
	VisitDepsDepthFirstIf(module Module, pred func(Module) bool,
		visit func(Module))

	VisitAllModuleVariants(module Module, visit func(Module))

	PrimaryModule(module Module) Module
	FinalModule(module Module) Module

	AddNinjaFileDeps(deps ...string)

	// GlobWithDeps returns a list of files that match the specified pattern but do not match any
	// of the patterns in excludes.  It also adds efficient dependencies to rerun the primary
	// builder whenever a file matching the pattern as added or removed, without rerunning if a
	// file that does not match the pattern is added to a searched directory.
	GlobWithDeps(pattern string, excludes []string) ([]string, error)

	Fs() pathtools.FileSystem
}

var _ SingletonContext = (*singletonContext)(nil)

type singletonContext struct {
	context *Context
	config  interface{}
	scope   *localScope
	globals *liveTracker

	ninjaFileDeps []string
	errs          []error

	actionDefs localBuildActions
}

func (s *singletonContext) Config() interface{} {
	return s.config
}

func (s *singletonContext) ModuleName(logicModule Module) string {
	return s.context.ModuleName(logicModule)
}

func (s *singletonContext) ModuleDir(logicModule Module) string {
	return s.context.ModuleDir(logicModule)
}

func (s *singletonContext) ModuleSubDir(logicModule Module) string {
	return s.context.ModuleSubDir(logicModule)
}

func (s *singletonContext) ModuleType(logicModule Module) string {
	return s.context.ModuleType(logicModule)
}

func (s *singletonContext) BlueprintFile(logicModule Module) string {
	return s.context.BlueprintFile(logicModule)
}

func (s *singletonContext) error(err error) {
	if err != nil {
		s.errs = append(s.errs, err)
	}
}

func (s *singletonContext) ModuleErrorf(logicModule Module, format string,
	args ...interface{}) {

	s.error(s.context.ModuleErrorf(logicModule, format, args...))
}

func (s *singletonContext) Errorf(format string, args ...interface{}) {
	// TODO: Make this not result in the error being printed as "internal error"
	s.error(fmt.Errorf(format, args...))
}

func (s *singletonContext) Failed() bool {
	return len(s.errs) > 0
}

func (s *singletonContext) Variable(pctx PackageContext, name, value string) {
	s.scope.ReparentTo(pctx)

	v, err := s.scope.AddLocalVariable(name, value)
	if err != nil {
		panic(err)
	}

	s.actionDefs.variables = append(s.actionDefs.variables, v)
}

func (s *singletonContext) Rule(pctx PackageContext, name string,
	params RuleParams, argNames ...string) Rule {

	s.scope.ReparentTo(pctx)

	r, err := s.scope.AddLocalRule(name, &params, argNames...)
	if err != nil {
		panic(err)
	}

	s.actionDefs.rules = append(s.actionDefs.rules, r)

	return r
}

func (s *singletonContext) Build(pctx PackageContext, params BuildParams) {
	s.scope.ReparentTo(pctx)

	def, err := parseBuildParams(s.scope, &params)
	if err != nil {
		panic(err)
	}

	s.actionDefs.buildDefs = append(s.actionDefs.buildDefs, def)
}

func (s *singletonContext) Eval(pctx PackageContext, str string) (string, error) {
	s.scope.ReparentTo(pctx)

	ninjaStr, err := parseNinjaString(s.scope, str)
	if err != nil {
		return "", err
	}

	err = s.globals.addNinjaStringDeps(ninjaStr)
	if err != nil {
		return "", err
	}

	return ninjaStr.Eval(s.globals.variables)
}

func (s *singletonContext) RequireNinjaVersion(major, minor, micro int) {
	s.context.requireNinjaVersion(major, minor, micro)
}

func (s *singletonContext) SetNinjaBuildDir(pctx PackageContext, value string) {
	s.scope.ReparentTo(pctx)

	ninjaValue, err := parseNinjaString(s.scope, value)
	if err != nil {
		panic(err)
	}

	s.context.setNinjaBuildDir(ninjaValue)
}

func (s *singletonContext) VisitAllModules(visit func(Module)) {
	s.context.VisitAllModules(visit)
}

func (s *singletonContext) VisitAllModulesIf(pred func(Module) bool,
	visit func(Module)) {

	s.context.VisitAllModulesIf(pred, visit)
}

func (s *singletonContext) VisitDepsDepthFirst(module Module,
	visit func(Module)) {

	s.context.VisitDepsDepthFirst(module, visit)
}

func (s *singletonContext) VisitDepsDepthFirstIf(module Module,
	pred func(Module) bool, visit func(Module)) {

	s.context.VisitDepsDepthFirstIf(module, pred, visit)
}

func (s *singletonContext) PrimaryModule(module Module) Module {
	return s.context.PrimaryModule(module)
}

func (s *singletonContext) FinalModule(module Module) Module {
	return s.context.FinalModule(module)
}

func (s *singletonContext) VisitAllModuleVariants(module Module, visit func(Module)) {
	s.context.VisitAllModuleVariants(module, visit)
}

func (s *singletonContext) AddNinjaFileDeps(deps ...string) {
	s.ninjaFileDeps = append(s.ninjaFileDeps, deps...)
}

func (s *singletonContext) GlobWithDeps(pattern string,
	excludes []string) ([]string, error) {
	return s.context.glob(pattern, excludes)
}

func (s *singletonContext) Fs() pathtools.FileSystem {
	return s.context.fs
}
<|endoftext|>


<commit_before>package user

import (
	"fmt"
	osuser "os/user"
	"path/filepath"

	"github.com/materials-commons/config"
)

var u *osuser.User

func init() {
	var err error
	if u, err = osuser.Current(); err != nil {
		panic(fmt.Sprintf("Couldn't determine current user: %s", err))
	}
}

func Home() string {
	return u.HomeDir
}

func APIKey() string {
	val, err := config.GetStringErr("apikey")
	if err != nil {
		panic(fmt.Sprintf("Unable to get APIKey: %s", err))
	}
	return val
}

func ConfigDir() string {
	return filepath.Join(u.HomeDir, ".materialscommons")
}

func ConfigFile() string {
	return filepath.Join(ConfigDir(), "config.json")
}
<commit_msg>Add comments and ProjectsFile method.<commit_after>package user

import (
	"fmt"
	osuser "os/user"
	"path/filepath"

	"github.com/materials-commons/config"
)

var u *osuser.User

func init() {
	var err error
	if u, err = osuser.Current(); err != nil {
		panic(fmt.Sprintf("Couldn't determine current user: %s", err))
	}
}

// Home returns the users home directory.
func Home() string {
	return u.HomeDir
}

// APIKey returns the users APIKey.
func APIKey() string {
	val, err := config.GetStringErr("apikey")
	if err != nil {
		panic(fmt.Sprintf("Unable to get APIKey: %s", err))
	}
	return val
}

// ConfigDir returns the path to the configuration directory. This
// directory is located at $HOME/.materialscommons.
func ConfigDir() string {
	return filepath.Join(u.HomeDir, ".materialscommons")
}

// ConfigFile returns the path to the configuration file. This
// path is ConfigDir()/config.json.
func ConfigFile() string {
	return filepath.Join(ConfigDir(), "config.json")
}

// ProjectsFile returns the path to the projects file. This file
// contains the list of projects, their location and id. The file
// is located at ConfigDir()/projects.json.
func ProjectsFile() string {
	return filepath.Join(ConfigDir(), "projects.json")
}
<|endoftext|>


<commit_before>package services

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/khades/servbot/models"
	"github.com/khades/servbot/repos"
)

type tdjTrack struct {
	Title string
}

// CheckTwitchDJTrack checks last playing track
func CheckTwitchDJTrack() {
	channels := repos.GetTwitchDJEnabledChannels()
	log.Println(len(channels))
	for _, channel := range channels {
		checkOneTwitchDJTrack(channel)
	}
}

func checkOneTwitchDJTrack(channel *models.ChannelInfo) {
	status := models.TwitchDJ{ID: channel.TwitchDJ.ID}
	defer repos.PushTwitchDJ(&channel.Channel, &status)
	resp, error := http.Get("https://twitch-dj.ru/includes/back.php?func=get_track&channel=" + channel.TwitchDJ.ID)
	defer resp.Body.Close()

	if error != nil {
		log.Println(error)
		return
	}
	track := tdjTrack{}
	marshallError := json.NewDecoder(resp.Body).Decode(&track)
	if marshallError != nil {
		log.Println(marshallError)
		return
	}
	if track.Title != "" {
		status.Playing = true
		status.Track = track.Title
	}
}
<commit_msg>clearing logs<commit_after>package services

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/khades/servbot/models"
	"github.com/khades/servbot/repos"
)

type tdjTrack struct {
	Title string
}

// CheckTwitchDJTrack checks last playing track
func CheckTwitchDJTrack() {
	channels := repos.GetTwitchDJEnabledChannels()
	for _, channel := range channels {
		checkOneTwitchDJTrack(channel)
	}
}

func checkOneTwitchDJTrack(channel *models.ChannelInfo) {
	status := models.TwitchDJ{ID: channel.TwitchDJ.ID}
	defer repos.PushTwitchDJ(&channel.Channel, &status)
	log.Printf("Checking %s twitchDj track \n", channel.Channel)
	resp, error := http.Get("https://twitch-dj.ru/includes/back.php?func=get_track&channel=" + channel.TwitchDJ.ID)
	defer resp.Body.Close()

	if error != nil {
		log.Println(error)
		return
	}
	track := tdjTrack{}
	marshallError := json.NewDecoder(resp.Body).Decode(&track)
	if marshallError != nil {
		log.Println(marshallError)
		return
	}
	if track.Title != "" {
		status.Playing = true
		status.Track = track.Title
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"os"
	"runtime"

	"github.com/ikawaha/waifu2x.go/engine"
)

const (
	modeAnime = "anime"
	modePhoto = "photo"
)

type option struct {
	// flagSet args
	input    string
	output   string
	scale    float64
	noise    int
	parallel int
	modeStr  string
	verbose  bool

	// option values
	mode    engine.Mode
	flagSet *flag.FlagSet
}

const commandName = `waifu2x`

func newOption(w io.Writer, eh flag.ErrorHandling) (o *option) {
	o = &option{
		flagSet: flag.NewFlagSet(commandName, eh),
	}
	// option settings
	o.flagSet.SetOutput(w)
	o.flagSet.StringVar(&o.input, "i", "", "input file (default stdin)")
	o.flagSet.StringVar(&o.output, "o", "", "output file (default stdout)")
	o.flagSet.Float64Var(&o.scale, "s", 2.0, "scale multiplier >= 1.0")
	o.flagSet.IntVar(&o.noise, "n", 0, "noise reduction level 0 <= n <= 3")
	o.flagSet.IntVar(&o.parallel, "p", runtime.GOMAXPROCS(runtime.NumCPU()), "concurrency")
	o.flagSet.StringVar(&o.modeStr, "m", modeAnime, "waifu2x mode, choose from 'anime' and 'photo'")
	o.flagSet.BoolVar(&o.verbose, "v", false, "verbose")
	return
}

func (o *option) parse(args []string) error {
	if err := o.flagSet.Parse(args); err != nil {
		return err
	}
	// validations
	if nonFlag := o.flagSet.Args(); len(nonFlag) != 0 {
		return fmt.Errorf("invalid argument: %v", nonFlag)
	}
	if o.scale < 1.0 {
		return fmt.Errorf("invalid scale, %v > 1", o.scale)
	}
	if o.noise < 0 || o.noise > 3 {
		return fmt.Errorf("invalid number of noise reduction level, it must be [0,3]")
	}
	if o.parallel < 1 {
		return fmt.Errorf("invalid number of parallel, it must be >= 1")
	}
	switch o.modeStr {
	case modeAnime:
		o.mode = engine.Anime
	case modePhoto:
		o.mode = engine.Photo
	default:
		return fmt.Errorf("invalid mode, choose from 'anime' or 'photo'")
	}
	return nil
}

func parseInputImage(file string) ([]byte, string, error) {
	r := os.Stdin
	if file != "" {
		var err error
		r, err = os.Open(file)
		if err != nil {
			return nil, "", err
		}
		defer r.Close()
	}
	b, err := io.ReadAll(r)
	if err != nil {
		return nil, "", err
	}
	_, format, err := image.DecodeConfig(bytes.NewReader(b))
	return b, format, err
}

func decodeImage(b []byte, format string) (image.Image, error) {
	var decoder func(io.Reader) (image.Image, error)
	switch format {
	case "jpeg":
		decoder = jpeg.Decode
	case "png":
		decoder = png.Decode
	default:
		return nil, fmt.Errorf("unsupported image type: %s", format)
	}
	return decoder(bytes.NewReader(b))
}

func scaleUp(ctx context.Context, w2x *engine.Waifu2x, img image.Image, scale float64, w io.Writer) error {
	ci, err := w2x.ScaleUp(ctx, img, scale)
	if err != nil {
		return err
	}
	rgba := ci.ImageRGBA()
	if err := png.Encode(w, &rgba); err != nil {
		return fmt.Errorf("output error: %w", err)
	}
	return nil
}

func scaleUpGIF(ctx context.Context, w2x *engine.Waifu2x, img *gif.GIF, scale float64, w io.Writer) error {
	g, err := w2x.ScaleUpGIF(ctx, img, scale)
	if err != nil {
		return err
	}
	if err := gif.EncodeAll(w, g); err != nil {
		return fmt.Errorf("output error: %w", err)
	}
	return nil
}

// Run executes the waifu2x command.
func Run(args []string) error {
	opt := newOption(os.Stderr, flag.ExitOnError)
	if err := opt.parse(args); err != nil {
		return err
	}
	b, format, err := parseInputImage(opt.input)
	if err != nil {
		return fmt.Errorf("input error: %w", err)
	}

	w2x, err := engine.NewWaifu2x(opt.mode, opt.noise, []engine.Option{
		engine.Verbose(opt.verbose),
		engine.Parallel(opt.parallel),
	}...)
	if err != nil {
		return err
	}
	var w io.Writer = os.Stdout
	if opt.output != "" {
		fp, err := os.Create(opt.output)
		if err != nil {
			return fmt.Errorf("output file, %w", err)
		}
		defer fp.Close()
		w = fp
	}
	if format != "gif" {
		img, err := decodeImage(b, format)
		if err != nil {
			return err
		}
		return scaleUp(context.TODO(), w2x, img, opt.scale, w)
	}
	img, err := gif.DecodeAll(bytes.NewReader(b))
	if err != nil {
		return err
	}
	return scaleUpGIF(context.TODO(), w2x, img, opt.scale, w)
}
<commit_msg>Set log output<commit_after>package cmd

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"os"
	"runtime"

	"github.com/ikawaha/waifu2x.go/engine"
)

const (
	modeAnime = "anime"
	modePhoto = "photo"
)

type option struct {
	// flagSet args
	input    string
	output   string
	scale    float64
	noise    int
	parallel int
	modeStr  string
	verbose  bool

	// option values
	mode    engine.Mode
	flagSet *flag.FlagSet
}

const commandName = `waifu2x`

func newOption(w io.Writer, eh flag.ErrorHandling) (o *option) {
	o = &option{
		flagSet: flag.NewFlagSet(commandName, eh),
	}
	// option settings
	o.flagSet.SetOutput(w)
	o.flagSet.StringVar(&o.input, "i", "", "input file (default stdin)")
	o.flagSet.StringVar(&o.output, "o", "", "output file (default stdout)")
	o.flagSet.Float64Var(&o.scale, "s", 2.0, "scale multiplier >= 1.0")
	o.flagSet.IntVar(&o.noise, "n", 0, "noise reduction level 0 <= n <= 3")
	o.flagSet.IntVar(&o.parallel, "p", runtime.GOMAXPROCS(runtime.NumCPU()), "concurrency")
	o.flagSet.StringVar(&o.modeStr, "m", modeAnime, "waifu2x mode, choose from 'anime' and 'photo'")
	o.flagSet.BoolVar(&o.verbose, "v", false, "verbose")
	return
}

func (o *option) parse(args []string) error {
	if err := o.flagSet.Parse(args); err != nil {
		return err
	}
	// validations
	if nonFlag := o.flagSet.Args(); len(nonFlag) != 0 {
		return fmt.Errorf("invalid argument: %v", nonFlag)
	}
	if o.scale < 1.0 {
		return fmt.Errorf("invalid scale, %v > 1", o.scale)
	}
	if o.noise < 0 || o.noise > 3 {
		return fmt.Errorf("invalid number of noise reduction level, it must be [0,3]")
	}
	if o.parallel < 1 {
		return fmt.Errorf("invalid number of parallel, it must be >= 1")
	}
	switch o.modeStr {
	case modeAnime:
		o.mode = engine.Anime
	case modePhoto:
		o.mode = engine.Photo
	default:
		return fmt.Errorf("invalid mode, choose from 'anime' or 'photo'")
	}
	return nil
}

func parseInputImage(file string) ([]byte, string, error) {
	r := os.Stdin
	if file != "" {
		var err error
		r, err = os.Open(file)
		if err != nil {
			return nil, "", err
		}
		defer r.Close()
	}
	b, err := io.ReadAll(r)
	if err != nil {
		return nil, "", err
	}
	_, format, err := image.DecodeConfig(bytes.NewReader(b))
	return b, format, err
}

func decodeImage(b []byte, format string) (image.Image, error) {
	var decoder func(io.Reader) (image.Image, error)
	switch format {
	case "jpeg":
		decoder = jpeg.Decode
	case "png":
		decoder = png.Decode
	default:
		return nil, fmt.Errorf("unsupported image type: %s", format)
	}
	return decoder(bytes.NewReader(b))
}

func scaleUp(ctx context.Context, w2x *engine.Waifu2x, img image.Image, scale float64, w io.Writer) error {
	ci, err := w2x.ScaleUp(ctx, img, scale)
	if err != nil {
		return err
	}
	rgba := ci.ImageRGBA()
	if err := png.Encode(w, &rgba); err != nil {
		return fmt.Errorf("output error: %w", err)
	}
	return nil
}

func scaleUpGIF(ctx context.Context, w2x *engine.Waifu2x, img *gif.GIF, scale float64, w io.Writer) error {
	g, err := w2x.ScaleUpGIF(ctx, img, scale)
	if err != nil {
		return err
	}
	if err := gif.EncodeAll(w, g); err != nil {
		return fmt.Errorf("output error: %w", err)
	}
	return nil
}

// Run executes the waifu2x command.
func Run(args []string) error {
	opt := newOption(os.Stderr, flag.ExitOnError)
	if err := opt.parse(args); err != nil {
		return err
	}
	b, format, err := parseInputImage(opt.input)
	if err != nil {
		return fmt.Errorf("input error: %w", err)
	}

	w2x, err := engine.NewWaifu2x(opt.mode, opt.noise, []engine.Option{
		engine.Verbose(opt.verbose),
		engine.Parallel(opt.parallel),
		engine.LogOutput(os.Stderr),
	}...)
	if err != nil {
		return err
	}
	var w io.Writer = os.Stdout
	if opt.output != "" {
		fp, err := os.Create(opt.output)
		if err != nil {
			return fmt.Errorf("output file, %w", err)
		}
		defer fp.Close()
		w = fp
	}
	if format != "gif" {
		img, err := decodeImage(b, format)
		if err != nil {
			return err
		}
		return scaleUp(context.TODO(), w2x, img, opt.scale, w)
	}
	img, err := gif.DecodeAll(bytes.NewReader(b))
	if err != nil {
		return err
	}
	return scaleUpGIF(context.TODO(), w2x, img, opt.scale, w)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/attilaolah/publicip"
)

func main() {
	ip, err := publicip.IP()
	if err != nil {
		fmt.Println("publicip: %v", err)
		os.Exit(1)
	}

	if ip == nil {
		panic("ip was nil")
	}

	fmt.Print(ip)
}
<commit_msg>print errors to stderr<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/attilaolah/publicip"
)

func main() {
	ip, err := publicip.IP()
	if err != nil {
		fmt.Errorf("publicip: %v", err)
		os.Exit(1)
	}

	if ip == nil {
		panic("ip was nil")
	}

	fmt.Print(ip)
}
<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC
 *
 *  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
 *
 *      https://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 controller is responsible for creating VMs in various regions, according to
 * the provided configuration string
 * The controller is also responsible for initiating probing on the VMs that are created
 */
package controller

import (
	"log"
	"sync"
	"time"

	"github.com/FirebaseExtended/fcm-external-prober/Probe/src/utils"
)

var (
	maker          utils.CommandMaker
	clock          utils.Timer
	logger		   Logger
	vms            map[string]*regionalVM
	stoppedVMs     int
	stoppedVMsLock sync.Mutex
	stopping       bool
	config         *ControllerConfig
)

type Controller struct{}

// Create a new controller with a provided configuration
func NewController(cfg *ControllerConfig, cmd utils.CommandMaker, clk utils.Timer, log Logger) *Controller {
	config = cfg
	maker = cmd
	clock = clk
	logger = log
	vms = make(map[string]*regionalVM)
	return &Controller{}
}

// Start gRPC server for regional VMs to connect to
func (ctrl *Controller) InitServer() {
	err := initServer()
	if err != nil {
		log.Fatalf("Controller: unable to start rpc server, %v", err)
	}
	err = addMetadata()
	if err != nil {
		log.Fatalf("Controller: unable to add project metadata %v", err)
	}
}

// Start all VMs in regions in which the required hardware is available, and for which there are probes specified
func (ctrl *Controller) InitProbes() {
	getPossibleZones()

	// Assign all probe configurations to their designated regions
	for _, p := range config.Probes.Probe {
		vm, ok := vms[p.GetRegion()+"-a"]
		if !ok {
			logger.LogErrorf("Controller: zone %s in region %s does not meet minimum requirements or does not exist", p.GetRegion()+"-a", p.GetRegion())
			continue
		}
		vm.probes = append(vm.probes, p)
	}

	// Start VMs in regions to which probes are designated, remove those that contain no probes
	for z, v := range vms {
		if len(v.probes) == 0 {
			delete(vms, z)
			continue
		}
		err := v.startVM()
		if err != nil {
			logger.LogErrorf("Controller: regional VM could not be started in zone %s", v.zone)
		}
	}
}

func getPossibleZones() {
	z, err := getCompatZones([]string{config.GetMinCpu()})
	if err != nil {
		log.Fatalf("Controller: unable to generate list of VM zones: %v", err)
	}
	for _, n := range z {
		// For now, the probe name is the same as the zone name. This will change if multiple VMs are required in a zone
		vms[n] = newRegionalVM(n, n)
		vms[n].setState(inactive)
	}
}

func (ctrl *Controller) MonitorProbes() {
	checkVMs(time.Duration(config.PingConfig.GetTimeout()) * time.Minute)
}
<commit_msg>replace log.Fatalf with logger.LogFatalf<commit_after>/*
 * Copyright 2020 Google LLC
 *
 *  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
 *
 *      https://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 controller is responsible for creating VMs in various regions, according to
 * the provided configuration string
 * The controller is also responsible for initiating probing on the VMs that are created
 */
package controller

import (
	"sync"
	"time"

	"github.com/FirebaseExtended/fcm-external-prober/Probe/src/utils"
)

var (
	maker          utils.CommandMaker
	clock          utils.Timer
	logger		   Logger
	vms            map[string]*regionalVM
	stoppedVMs     int
	stoppedVMsLock sync.Mutex
	stopping       bool
	config         *ControllerConfig
)

type Controller struct{}

// Create a new controller with a provided configuration
func NewController(cfg *ControllerConfig, cmd utils.CommandMaker, clk utils.Timer, log Logger) *Controller {
	config = cfg
	maker = cmd
	clock = clk
	logger = log
	vms = make(map[string]*regionalVM)
	return &Controller{}
}

// Start gRPC server for regional VMs to connect to
func (ctrl *Controller) InitServer() {
	err := initServer()
	if err != nil {
		logger.LogFatalf("Controller: unable to start rpc server, %v", err)
	}
	err = addMetadata()
	if err != nil {
		logger.LogFatalf("Controller: unable to add project metadata %v", err)
	}
}

// Start all VMs in regions in which the required hardware is available, and for which there are probes specified
func (ctrl *Controller) InitProbes() {
	getPossibleZones()

	// Assign all probe configurations to their designated regions
	for _, p := range config.Probes.Probe {
		vm, ok := vms[p.GetRegion()+"-a"]
		if !ok {
			logger.LogErrorf("Controller: zone %s in region %s does not meet minimum requirements or does not exist", p.GetRegion()+"-a", p.GetRegion())
			continue
		}
		vm.probes = append(vm.probes, p)
	}

	// Start VMs in regions to which probes are designated, remove those that contain no probes
	for z, v := range vms {
		if len(v.probes) == 0 {
			delete(vms, z)
			continue
		}
		err := v.startVM()
		if err != nil {
			logger.LogErrorf("Controller: regional VM could not be started in zone %s", v.zone)
		}
	}
}

func getPossibleZones() {
	z, err := getCompatZones([]string{config.GetMinCpu()})
	if err != nil {
		logger.LogFatalf("Controller: unable to generate list of VM zones: %v", err)
	}
	for _, n := range z {
		// For now, the probe name is the same as the zone name. This will change if multiple VMs are required in a zone
		vms[n] = newRegionalVM(n, n)
		vms[n].setState(inactive)
	}
}

func (ctrl *Controller) MonitorProbes() {
	checkVMs(time.Duration(config.PingConfig.GetTimeout()) * time.Minute)
}
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"os"

	"github.com/kfeofantov/chkit-v2/chlib"
	"github.com/kfeofantov/chkit-v2/helpers"
	"github.com/spf13/cobra"
	jww "github.com/spf13/jwalterweatherman"
)

var getCmdFile, getCmdKind, getCmdName string

var getCmd = &cobra.Command{
	Use:   "get (KIND | --file -f FILE)",
	Short: "Show user`s objects",
	PreRun: func(cmd *cobra.Command, args []string) {
		if cmd.Flag("output").Changed {
			switch val, _ := cmd.Flags().GetString("output"); val {
			case "json", "yaml", "pretty":
			default:
				jww.FEEDBACK.Println("output must be json, yaml or pretty")
				os.Exit(1)
			}
		}
		if len(args) == 0 {
			jww.FEEDBACK.Println("KIND or file not specified")
			os.Exit(1)
		}
		switch args[0] {
		case "--file", "-f":
			getCmdFile = args[1]
		case "po", "pods", "pod", "deployments", "deployment", "deploy", "service", "services", "svc", "ns", "namespaces", "namespace":
			getCmdKind = args[0]
			if len(args) >= 2 && args[2][0] != '-' {
				getCmdName = args[2]
			}
		default:
			jww.FEEDBACK.Println("Invalid KIND (choose from 'po', 'pods', 'pod', 'deployments', 'deployment', 'deploy', 'service', 'services', 'svc', 'ns', 'namespaces', 'namespace') or file")
			os.Exit(1)
		}

	},
	Run: func(cmd *cobra.Command, args []string) {
		client, err := chlib.NewClient(helpers.CurrentClientVersion, helpers.UuidV4())
		if err != nil {
			jww.ERROR.Println(err)
			return
		}
		var jsonContent chlib.GenericJson
		if getCmdFile != "" {
			jsonContent, err = chlib.LoadGenericJsonFromFile(getCmdFile)
		} else {
			jsonContent, err = chlib.GetCmdRequestJson(client, getCmdKind, getCmdName)
		}
		if err != nil {
			jww.ERROR.Printf("json receive error: %s\n", err)
			return
		}

		switch format, _ := cmd.Flags().GetString("output"); format {
		case "pretty":
			var ppc chlib.PrettyPrintConfig
			switch getCmdKind {
			case "ns", "namespaces", "namespace":
				nsResults, err := chlib.ExtractNsResults(jsonContent)
				if err != nil {
					jww.ERROR.Println(err)
				}
				ppc, err = chlib.FormatNamespacePrettyPrint(nsResults)
			}
			if err != nil {
				jww.ERROR.Println(err)
				return
			}
			chlib.PrettyPrint(ppc, os.Stdout)
		case "json":
			b, _ := json.MarshalIndent(jsonContent, "", "    ")
			jww.FEEDBACK.Printf("%s\n", b)
		case "yaml":

		}
	},
}

func init() {
	getCmd.PersistentFlags().StringP("output", "o", "pretty", "Output format: json, yaml, pretty")
	getCmd.PersistentFlags().StringP("namespace", "n", chlib.DefaultNameSpace, "Namespace")
	getCmd.PersistentFlags().StringP("file", "f", "", "JSON file generated on object creation")
	RootCmd.AddCommand(getCmd)
}
<commit_msg>Add yaml printing<commit_after>package cmd

import (
	"encoding/json"
	"os"

	"github.com/kfeofantov/chkit-v2/chlib"
	"github.com/kfeofantov/chkit-v2/helpers"
	"github.com/spf13/cobra"
	jww "github.com/spf13/jwalterweatherman"
	"gopkg.in/yaml.v2"
)

var getCmdFile, getCmdKind, getCmdName string

var getCmd = &cobra.Command{
	Use:   "get (KIND | --file -f FILE)",
	Short: "Show user`s objects",
	PreRun: func(cmd *cobra.Command, args []string) {
		if cmd.Flag("output").Changed {
			switch val, _ := cmd.Flags().GetString("output"); val {
			case "json", "yaml", "pretty":
			default:
				jww.FEEDBACK.Println("output must be json, yaml or pretty")
				os.Exit(1)
			}
		}
		if len(args) == 0 {
			jww.FEEDBACK.Println("KIND or file not specified")
			os.Exit(1)
		}
		switch args[0] {
		case "--file", "-f":
			getCmdFile = args[1]
		case "po", "pods", "pod", "deployments", "deployment", "deploy", "service", "services", "svc", "ns", "namespaces", "namespace":
			getCmdKind = args[0]
			if len(args) >= 2 && args[2][0] != '-' {
				getCmdName = args[2]
			}
		default:
			jww.FEEDBACK.Println("Invalid KIND (choose from 'po', 'pods', 'pod', 'deployments', 'deployment', 'deploy', 'service', 'services', 'svc', 'ns', 'namespaces', 'namespace') or file")
			os.Exit(1)
		}

	},
	Run: func(cmd *cobra.Command, args []string) {
		client, err := chlib.NewClient(helpers.CurrentClientVersion, helpers.UuidV4())
		if err != nil {
			jww.ERROR.Println(err)
			return
		}
		var jsonContent chlib.GenericJson
		if getCmdFile != "" {
			jsonContent, err = chlib.LoadGenericJsonFromFile(getCmdFile)
		} else {
			jsonContent, err = chlib.GetCmdRequestJson(client, getCmdKind, getCmdName)
		}
		if err != nil {
			jww.ERROR.Printf("json receive error: %s\n", err)
			return
		}

		switch format, _ := cmd.Flags().GetString("output"); format {
		case "pretty":
			var ppc chlib.PrettyPrintConfig
			switch getCmdKind {
			case "ns", "namespaces", "namespace":
				nsResults, err := chlib.ExtractNsResults(jsonContent)
				if err != nil {
					jww.ERROR.Println(err)
				}
				ppc, err = chlib.FormatNamespacePrettyPrint(nsResults)
			}
			if err != nil {
				jww.ERROR.Println(err)
				return
			}
			chlib.PrettyPrint(ppc, os.Stdout)
		case "json":
			b, _ := json.MarshalIndent(jsonContent, "", "    ")
			jww.FEEDBACK.Printf("%s\n", b)
		case "yaml":
			b, _ := yaml.Marshal(jsonContent)
			jww.FEEDBACK.Printf("%s\n", b)
		}
	},
}

func init() {
	getCmd.PersistentFlags().StringP("output", "o", "pretty", "Output format: json, yaml, pretty")
	getCmd.PersistentFlags().StringP("namespace", "n", chlib.DefaultNameSpace, "Namespace")
	getCmd.PersistentFlags().StringP("file", "f", "", "JSON file generated on object creation")
	RootCmd.AddCommand(getCmd)
}
<|endoftext|>


<commit_before>// 擷取盤中即時資訊與大盤、上櫃、寶島指數.
//
/*

Install:

	go install github.com/toomore/gogrs/cmd/realtime

Usage:

	realtime [flags]

The flags are:

	-twse
		上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329
	-otc
		上櫃股票代碼，可使用 ',' 分隔多組代碼，例：8446,2719
	-index
		顯示大盤、上櫃、寶島指數（default: false）
	-twsecate
		上市股票類別，可使用 ',' 分隔多組代碼，例：11,15
	-showtwsecatelist
		顯示上市分類表（default: false）
	-ncpu
		指定 CPU 數量，預設為實際 CPU 數量
	-pt
		計算花費時間

範例

	realtime -twse=2618,2329 -otc=8446,2719 -index

回傳內容

	2015/04/29 23:43:13 櫃買指數(o00) $144.72(-1.15) 1271/23250 [2015-04-29 13:33:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 發行量加權股價指數(t00) $9853.83(-80.99) 5742/113762 [2015-04-29 13:33:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 寶島股價指數(FRMSA) $11416.04(-93.58) 0/0 [2015-04-29 13:33:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 華研(8446) $124.00(1.00) 6/293 [2015-04-29 14:30:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 長榮航(2618) $24.20(-0.55) 666/15618 [2015-04-29 14:30:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 燦星旅(2719) $24.15(0.25) 6/83 [2015-04-29 14:30:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 華泰(2329) $15.95(0.50) 648/19995 [2015-04-29 14:30:00 +0800 CST] [20150429 23:43:13]

*/
package main

import (
	"flag"
	"fmt"
	"log"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/toomore/gogrs/realtime"
	"github.com/toomore/gogrs/tradingdays"
	"github.com/toomore/gogrs/twse"
	"github.com/toomore/gogrs/utils"
)

var cacheTime time.Time

// TaipeiNow show Taipei Now time.
func TaipeiNow() time.Time {
	if cacheTime.Year() == 1 {
		d := time.Now().UTC()
		days := d.Day()
		for {
			if tradingdays.IsOpen(d.Year(), d.Month(), days) {
				break
			}
			days--
		}
		if d.Before(time.Date(d.Year(), d.Month(), days, 1, 0, 0, 0, time.FixedZone("UTC", 0))) {
			if d.Hour() == 0 && d.Minute() <= 59 {
				days--
			}
		}
		cacheTime = time.Date(d.Year(), d.Month(), days, 0, 0, 0, 0, utils.TaipeiTimeZone)
	}
	return cacheTime
}

func prettyprint(data realtime.Data) string {
	return fmt.Sprintf("%s(%s) $%.2f(%.2f) %.0f/%.0f [%s] [%s %s]",
		data.Info.Name, data.Info.No,
		data.Price, data.Price-data.Open, data.Volume, data.VolumeAcc,
		data.TradeTime, data.SysInfo["sysDate"], data.SysInfo["sysTime"])
}

var chanbuf int

var twseNo = flag.String("twse", "", "上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329")
var twseCate = flag.String("twsecate", "", "上市股票類別，可使用 ',' 分隔多組代碼，例：11,15")
var showtwsecatelist = flag.Bool("showcatelist", false, "顯示上市分類表")
var otcNo = flag.String("otc", "", "上櫃股票代碼，可使用 ',' 分隔多組代碼，例：8446,2719")
var index = flag.Bool("index", false, "顯示大盤、上櫃、寶島指數（default: false）")
var ncpu = flag.Int("ncpu", runtime.NumCPU(), "指定 CPU 數量，預設為實際 CPU 數量")
var pt = flag.Bool("pt", false, "計算花費時間")

func main() {
	flag.Parse()

	runtime.GOMAXPROCS(*ncpu)
	chanbuf = *ncpu * 2

	queue := make(chan *realtime.StockRealTime, chanbuf)
	defer close(queue)
	var wg sync.WaitGroup

	var startTime time.Time
	if *pt {
		startTime = time.Now()
	}

	if *showtwsecatelist {
		var index = 1
		for cateNo, cateName := range twse.TWSECLASS {
			fmt.Printf("%s(%s)\t\t", cateName, cateNo)
			if index%3 == 0 {
				fmt.Println("")
			}
			index++
		}
		fmt.Println("")
	}

	if *twseNo != "" {
		for _, no := range strings.Split(*twseNo, ",") {
			wg.Add(1)
			go func(no string) {
				runtime.Gosched()
				queue <- realtime.NewTWSE(no, TaipeiNow())
			}(no)
		}
	}

	if *twseCate != "" {
		l := &twse.Lists{Date: TaipeiNow()}
		for _, no := range strings.Split(*twseCate, ",") {
			for _, s := range l.GetCategoryList(no) {
				wg.Add(1)
				go func(s twse.StockInfo) {
					runtime.Gosched()
					queue <- realtime.NewTWSE(s.No, TaipeiNow())
				}(s)
			}
		}
	}

	if *otcNo != "" {
		for _, no := range strings.Split(*otcNo, ",") {
			wg.Add(1)
			go func(no string) {
				runtime.Gosched()
				queue <- realtime.NewOTC(no, TaipeiNow())
			}(no)
		}
	}
	if *index {
		wg.Add(3)
		for _, r := range []*realtime.StockRealTime{realtime.NewWeight(TaipeiNow()), realtime.NewOTCI(TaipeiNow()), realtime.NewFRMSA(TaipeiNow())} {
			go func(r *realtime.StockRealTime) {
				runtime.Gosched()
				queue <- r
			}(r)
		}
	}
	go func() {
		for r := range queue {
			go func(r *realtime.StockRealTime) {
				defer wg.Done()
				runtime.Gosched()
				data, _ := r.Get()
				log.Println(prettyprint(data))
			}(r)
		}
	}()
	wg.Wait()
	if *pt {
		defer fmt.Println(time.Now().Sub(startTime))
	}
	if flag.NFlag() == 0 {
		flag.PrintDefaults()
	}
}
<commit_msg>Fixed cmd realtime get twse cate in wrong date.<commit_after>// 擷取盤中即時資訊與大盤、上櫃、寶島指數.
//
/*

Install:

	go install github.com/toomore/gogrs/cmd/realtime

Usage:

	realtime [flags]

The flags are:

	-twse
		上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329
	-otc
		上櫃股票代碼，可使用 ',' 分隔多組代碼，例：8446,2719
	-index
		顯示大盤、上櫃、寶島指數（default: false）
	-twsecate
		上市股票類別，可使用 ',' 分隔多組代碼，例：11,15
	-showtwsecatelist
		顯示上市分類表（default: false）
	-ncpu
		指定 CPU 數量，預設為實際 CPU 數量
	-pt
		計算花費時間

範例

	realtime -twse=2618,2329 -otc=8446,2719 -index

回傳內容

	2015/04/29 23:43:13 櫃買指數(o00) $144.72(-1.15) 1271/23250 [2015-04-29 13:33:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 發行量加權股價指數(t00) $9853.83(-80.99) 5742/113762 [2015-04-29 13:33:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 寶島股價指數(FRMSA) $11416.04(-93.58) 0/0 [2015-04-29 13:33:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 華研(8446) $124.00(1.00) 6/293 [2015-04-29 14:30:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 長榮航(2618) $24.20(-0.55) 666/15618 [2015-04-29 14:30:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 燦星旅(2719) $24.15(0.25) 6/83 [2015-04-29 14:30:00 +0800 CST] [20150429 23:43:13]
	2015/04/29 23:43:13 華泰(2329) $15.95(0.50) 648/19995 [2015-04-29 14:30:00 +0800 CST] [20150429 23:43:13]

*/
package main

import (
	"flag"
	"fmt"
	"log"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/toomore/gogrs/realtime"
	"github.com/toomore/gogrs/tradingdays"
	"github.com/toomore/gogrs/twse"
	"github.com/toomore/gogrs/utils"
)

var cacheTime time.Time

// TaipeiNow show Taipei Now time.
func TaipeiNow() time.Time {
	if cacheTime.Year() == 1 {
		d := time.Now().UTC()
		days := d.Day()
		for {
			if tradingdays.IsOpen(d.Year(), d.Month(), days) {
				break
			}
			days--
		}
		if d.Before(time.Date(d.Year(), d.Month(), days, 1, 0, 0, 0, time.FixedZone("UTC", 0))) {
			if d.Hour() == 0 && d.Minute() <= 59 {
				days--
			}
		}
		cacheTime = time.Date(d.Year(), d.Month(), days, 0, 0, 0, 0, utils.TaipeiTimeZone)
	}
	return cacheTime
}

func prettyprint(data realtime.Data) string {
	return fmt.Sprintf("%s(%s) $%.2f(%.2f) %.0f/%.0f [%s] [%s %s]",
		data.Info.Name, data.Info.No,
		data.Price, data.Price-data.Open, data.Volume, data.VolumeAcc,
		data.TradeTime, data.SysInfo["sysDate"], data.SysInfo["sysTime"])
}

var chanbuf int

var twseNo = flag.String("twse", "", "上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329")
var twseCate = flag.String("twsecate", "", "上市股票類別，可使用 ',' 分隔多組代碼，例：11,15")
var showtwsecatelist = flag.Bool("showcatelist", false, "顯示上市分類表")
var otcNo = flag.String("otc", "", "上櫃股票代碼，可使用 ',' 分隔多組代碼，例：8446,2719")
var index = flag.Bool("index", false, "顯示大盤、上櫃、寶島指數（default: false）")
var ncpu = flag.Int("ncpu", runtime.NumCPU(), "指定 CPU 數量，預設為實際 CPU 數量")
var pt = flag.Bool("pt", false, "計算花費時間")

func main() {
	flag.Parse()

	runtime.GOMAXPROCS(*ncpu)
	chanbuf = *ncpu * 2

	queue := make(chan *realtime.StockRealTime, chanbuf)
	defer close(queue)
	var wg sync.WaitGroup

	var startTime time.Time
	if *pt {
		startTime = time.Now()
	}

	if *showtwsecatelist {
		var index = 1
		for cateNo, cateName := range twse.TWSECLASS {
			fmt.Printf("%s(%s)\t\t", cateName, cateNo)
			if index%3 == 0 {
				fmt.Println("")
			}
			index++
		}
		fmt.Println("")
	}

	if *twseNo != "" {
		for _, no := range strings.Split(*twseNo, ",") {
			wg.Add(1)
			go func(no string) {
				runtime.Gosched()
				queue <- realtime.NewTWSE(no, TaipeiNow())
			}(no)
		}
	}

	if *twseCate != "" {
		l := &twse.Lists{Date: tradingdays.FindRecentlyOpened()}
		for _, no := range strings.Split(*twseCate, ",") {
			for _, s := range l.GetCategoryList(no) {
				wg.Add(1)
				go func(s twse.StockInfo) {
					runtime.Gosched()
					queue <- realtime.NewTWSE(s.No, TaipeiNow())
				}(s)
			}
		}
	}

	if *otcNo != "" {
		for _, no := range strings.Split(*otcNo, ",") {
			wg.Add(1)
			go func(no string) {
				runtime.Gosched()
				queue <- realtime.NewOTC(no, TaipeiNow())
			}(no)
		}
	}
	if *index {
		wg.Add(3)
		for _, r := range []*realtime.StockRealTime{realtime.NewWeight(TaipeiNow()), realtime.NewOTCI(TaipeiNow()), realtime.NewFRMSA(TaipeiNow())} {
			go func(r *realtime.StockRealTime) {
				runtime.Gosched()
				queue <- r
			}(r)
		}
	}
	go func() {
		for r := range queue {
			go func(r *realtime.StockRealTime) {
				defer wg.Done()
				runtime.Gosched()
				data, _ := r.Get()
				log.Println(prettyprint(data))
			}(r)
		}
	}()
	wg.Wait()
	if *pt {
		defer fmt.Println(time.Now().Sub(startTime))
	}
	if flag.NFlag() == 0 {
		flag.PrintDefaults()
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/colorjson"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/minio-go/v7/pkg/replication"
	"github.com/minio/pkg/console"
)

var replicateAddFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "arn",
		Usage: "unique role ARN",
	},
	cli.StringFlag{
		Name:  "id",
		Usage: "id for the rule, should be a unique value",
	},
	cli.StringFlag{
		Name:  "tags",
		Usage: "format '<key1>=<value1>&<key2>=<value2>&<key3>=<value3>', multiple values allowed for multiple key/value pairs",
	},
	cli.StringFlag{
		Name:  "storage-class",
		Usage: "storage class for destination (STANDARD_IA,REDUCED_REDUNDANCY etc)",
	},
	cli.BoolFlag{
		Name:  "disable",
		Usage: "disable the rule",
	},
	cli.IntFlag{
		Name:  "priority",
		Usage: "priority of the rule, should be unique and is a required field",
	},
	cli.StringFlag{
		Name:  "remote-bucket",
		Usage: "remote bucket, should be a unique value for the configuration",
	},
	cli.StringFlag{
		Name:  "replicate",
		Usage: "comma separated list to enable replication of delete markers, deletion of versioned objects and replica metadata sync(in the case of active-active replication).Valid options are \"delete-marker\", \"delete\" ,\"replica-metadata-sync\", \"existing-objects\" and \"\"",
	},
}

var replicateAddCmd = cli.Command{
	Name:         "add",
	Usage:        "add a server side replication configuration rule",
	Action:       mainReplicateAdd,
	OnUsageError: onUsageError,
	Before:       setGlobalsFromContext,
	Flags:        append(globalFlags, replicateAddFlags...),
	CustomHelpTemplate: `NAME:
 {{.HelpName}} - {{.Usage}}

USAGE:
 {{.HelpName}} TARGET

FLAGS:
 {{range .VisibleFlags}}{{.}}
 {{end}}
EXAMPLES:
 1. Add replication configuration rule on bucket "mybucket" for alias "myminio" to replicate all objects with tags
    "key1=value1, key2=value2" to destbucket, including delete markers and versioned deletes.
	{{.Prompt}} {{.HelpName}} myminio/mybucket/prefix --tags "key1=value1&key2=value2" \
			--remote-bucket 'arn:minio:replica::c5be6b16-769d-432a-9ef1-4567081f3566:destbucket' \
			--storage-class "STANDARD" \
			--priority 1 \
			--replicate "delete,delete-marker"

 2. Add replication configuration rule with Disabled status on bucket "mybucket" for alias "myminio".
    {{.Prompt}} {{.HelpName}} myminio/mybucket/prefix --tags "key1=value1&key2=value2" \
        --storage-class "STANDARD" --disable \
        --remote-bucket 'arn:minio:replica::c5be6b16-769d-432a-9ef1-4567081f3566:destbucket' \
        --priority 1

 3. Add replication configuration rule with existing object replication, delete marker replication and versioned deletes
 	 enabled on bucket "mybucket" for alias "myminio".
	 {{.Prompt}} {{.HelpName}} myminio/mybucket/prefix --tags "key1=value1&key2=value2" \
		--storage-class "STANDARD"  \
		--remote-bucket 'arn:minio:replica::c5be6b16-769d-432a-9ef1-4567081f3566:destbucket' \
		--priority 1 \
		--replicate "existing-objects,delete,delete-marker"
`,
}

// checkReplicateAddSyntax - validate all the passed arguments
func checkReplicateAddSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		cli.ShowCommandHelpAndExit(ctx, "add", 1) // last argument is exit code
	}
	if ctx.String("remote-bucket") == "" {
		fatal(errDummy().Trace(), "--remote-bucket flag needs to be specified.")
	}
}

type replicateAddMessage struct {
	Op     string `json:"op"`
	Status string `json:"status"`
	URL    string `json:"url"`
	ID     string `json:"id"`
}

const (
	enableStatus  = "enable"
	disableStatus = "disable"
)

func (l replicateAddMessage) JSON() string {
	l.Status = "success"
	jsonMessageBytes, e := json.MarshalIndent(l, "", " ")
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(jsonMessageBytes)
}

func (l replicateAddMessage) String() string {
	if l.ID != "" {
		return console.Colorize("replicateAddMessage", "Replication configuration rule with ID `"+l.ID+"` applied to "+l.URL+".")
	}
	return console.Colorize("replicateAddMessage", "Replication configuration rule applied to "+l.URL+" successfully.")
}

func mainReplicateAdd(cliCtx *cli.Context) error {
	ctx, cancelReplicateAdd := context.WithCancel(globalContext)
	defer cancelReplicateAdd()

	console.SetColor("replicateAddMessage", color.New(color.FgGreen))

	checkReplicateAddSyntax(cliCtx)

	// Get the alias parameter from cli
	args := cliCtx.Args()
	aliasedURL := args.Get(0)
	// Create a new Client
	client, err := newClient(aliasedURL)
	fatalIf(err, "Unable to initialize connection.")
	rcfg, err := client.GetReplication(ctx)
	fatalIf(err.Trace(args...), "Unable to get replication configuration")
	ruleStatus := enableStatus
	if cliCtx.Bool(disableStatus) {
		ruleStatus = disableStatus
	}
	dmReplicateStatus := disableStatus
	deleteReplicationStatus := disableStatus
	replicaSync := enableStatus
	existingReplicationStatus := disableStatus
	if cliCtx.IsSet("replicate") {
		replSlice := strings.Split(cliCtx.String("replicate"), ",")
		for _, opt := range replSlice {
			switch strings.TrimSpace(strings.ToLower(opt)) {
			case "delete-marker":
				dmReplicateStatus = enableStatus
			case "delete":
				deleteReplicationStatus = enableStatus
			case "replica-metadata-sync":
				replicaSync = enableStatus
			case "existing-objects":
				existingReplicationStatus = enableStatus

			default:
				fatalIf(probe.NewError(fmt.Errorf("invalid value for --replicate flag %s", cliCtx.String("replicate"))), "--replicate flag takes one or more comma separated string with values \"delete, delete-marker, replica-metadata-sync\",\"existing-objects\" or \"\" to disable these settings")
			}
		}
	}

	opts := replication.Options{
		TagString:               cliCtx.String("tags"),
		RoleArn:                 cliCtx.String("arn"),
		StorageClass:            cliCtx.String("storage-class"),
		Priority:                strconv.Itoa(cliCtx.Int("priority")),
		RuleStatus:              ruleStatus,
		ID:                      cliCtx.String("id"),
		DestBucket:              cliCtx.String("remote-bucket"),
		Op:                      replication.AddOption,
		ReplicateDeleteMarkers:  dmReplicateStatus,
		ReplicateDeletes:        deleteReplicationStatus,
		ReplicaSync:             replicaSync,
		ExistingObjectReplicate: existingReplicationStatus,
	}
	fatalIf(client.SetReplication(ctx, &rcfg, opts), "Could not add replication rule")
	printMsg(replicateAddMessage{
		Op:  "add",
		URL: aliasedURL,
		ID:  opts.ID,
	})
	return nil
}
<commit_msg>simplify wording for `mc replicate add` and default sane values (#3978)<commit_after>// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package cmd

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"github.com/fatih/color"
	"github.com/minio/cli"
	json "github.com/minio/colorjson"
	"github.com/minio/mc/pkg/probe"
	"github.com/minio/minio-go/v7/pkg/replication"
	"github.com/minio/pkg/console"
)

var replicateAddFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "arn",
		Usage: "unique role ARN",
	},
	cli.StringFlag{
		Name:  "id",
		Usage: "id for the rule, should be a unique value",
	},
	cli.StringFlag{
		Name:  "tags",
		Usage: "format '<key1>=<value1>&<key2>=<value2>&<key3>=<value3>', multiple values allowed for multiple key/value pairs",
	},
	cli.StringFlag{
		Name:  "storage-class",
		Usage: `storage class for destination, valid values are either "STANDARD" or "REDUCED_REDUNDANCY"`,
	},
	cli.BoolFlag{
		Name:  "disable",
		Usage: "disable the rule",
	},
	cli.IntFlag{
		Name:  "priority",
		Usage: "priority of the rule, should be unique and is a required field",
	},
	cli.StringFlag{
		Name:  "remote-bucket",
		Usage: "remote bucket, should be a unique value for the configuration",
	},
	cli.StringFlag{
		Name:  "replicate",
		Value: `delete-marker,delete,existing-objects,metadata-sync`,
		Usage: `comma separated list to enable replication of soft deletes, permanent deletes, existing objects and metadata sync`,
	},
}

var replicateAddCmd = cli.Command{
	Name:         "add",
	Usage:        "add a server side replication configuration rule",
	Action:       mainReplicateAdd,
	OnUsageError: onUsageError,
	Before:       setGlobalsFromContext,
	Flags:        append(globalFlags, replicateAddFlags...),
	CustomHelpTemplate: `NAME:
 {{.HelpName}} - {{.Usage}}

USAGE:
 {{.HelpName}} TARGET

FLAGS:
 {{range .VisibleFlags}}{{.}}
 {{end}}
EXAMPLES:
 1. Add replication configuration rule on bucket "mybucket" for alias "myminio" to replicate all operations in an active-active replication setup.
    {{.Prompt}} {{.HelpName}} myminio/mybucket --remote-bucket "arn:minio:replica::c5be6b16-769d-432a-9ef1-4567081f3566:destbucket" \
        --priority 1

 2. Add replication configuration rule on bucket "mybucket" for alias "myminio" to replicate all objects with tags
    "key1=value1, key2=value2" to destbucket.
    {{.Prompt}} {{.HelpName}} myminio/mybucket --remote-bucket "arn:minio:replica::c5be6b16-769d-432a-9ef1-4567081f3566:destbucket" \
        --tags "key1=value1&key2=value2" \
        --priority 1

 3. Disable a replication configuration rule on bucket "mybucket" for alias "myminio".
    {{.Prompt}} {{.HelpName}} myminio/mybucket --remote-bucket "arn:minio:replica::c5be6b16-769d-432a-9ef1-4567081f3566:destbucket" \
        --tags "key1=value1&key2=value2" \
        --priority 1 --disable

 4. Add replication configuration rule with existing object replication, delete marker replication and versioned deletes
    enabled on bucket "mybucket" for alias "myminio".
    {{.Prompt}} {{.HelpName}} myminio/mybucket --remote-bucket "arn:minio:replica::c5be6b16-769d-432a-9ef1-4567081f3566:destbucket" \
        --replicate "existing-objects,delete,delete-marker" \
        --priority 1
`,
}

// checkReplicateAddSyntax - validate all the passed arguments
func checkReplicateAddSyntax(ctx *cli.Context) {
	if len(ctx.Args()) != 1 {
		cli.ShowCommandHelpAndExit(ctx, "add", 1) // last argument is exit code
	}
	if ctx.String("remote-bucket") == "" {
		fatal(errDummy().Trace(), "--remote-bucket flag needs to be specified.")
	}
}

type replicateAddMessage struct {
	Op     string `json:"op"`
	Status string `json:"status"`
	URL    string `json:"url"`
	ID     string `json:"id"`
}

const (
	enableStatus  = "enable"
	disableStatus = "disable"
)

func (l replicateAddMessage) JSON() string {
	l.Status = "success"
	jsonMessageBytes, e := json.MarshalIndent(l, "", " ")
	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
	return string(jsonMessageBytes)
}

func (l replicateAddMessage) String() string {
	if l.ID != "" {
		return console.Colorize("replicateAddMessage", "Replication configuration rule with ID `"+l.ID+"` applied to "+l.URL+".")
	}
	return console.Colorize("replicateAddMessage", "Replication configuration rule applied to "+l.URL+" successfully.")
}

func mainReplicateAdd(cliCtx *cli.Context) error {
	ctx, cancelReplicateAdd := context.WithCancel(globalContext)
	defer cancelReplicateAdd()

	console.SetColor("replicateAddMessage", color.New(color.FgGreen))

	checkReplicateAddSyntax(cliCtx)

	// Get the alias parameter from cli
	args := cliCtx.Args()
	aliasedURL := args.Get(0)
	// Create a new Client
	client, err := newClient(aliasedURL)
	fatalIf(err, "Unable to initialize connection.")
	rcfg, err := client.GetReplication(ctx)
	fatalIf(err.Trace(args...), "Unable to get replication configuration")
	ruleStatus := enableStatus
	if cliCtx.Bool(disableStatus) {
		ruleStatus = disableStatus
	}
	dmReplicateStatus := disableStatus
	deleteReplicationStatus := disableStatus
	replicaSync := enableStatus
	existingReplicationStatus := disableStatus
	if cliCtx.IsSet("replicate") {
		replSlice := strings.Split(cliCtx.String("replicate"), ",")
		for _, opt := range replSlice {
			switch strings.TrimSpace(strings.ToLower(opt)) {
			case "delete-marker":
				dmReplicateStatus = enableStatus
			case "delete":
				deleteReplicationStatus = enableStatus
			case "metadata-sync", "replica-metadata-sync":
				replicaSync = enableStatus
			case "existing-objects":
				existingReplicationStatus = enableStatus
			default:
				fatalIf(probe.NewError(fmt.Errorf("invalid value for --replicate flag %s", cliCtx.String("replicate"))), `--replicate flag takes one or more comma separated string with values "delete", "delete-marker", "metadata-sync", "existing-objects" or "" to disable these settings`)
			}
		}
	}

	opts := replication.Options{
		TagString:               cliCtx.String("tags"),
		RoleArn:                 cliCtx.String("arn"),
		StorageClass:            cliCtx.String("storage-class"),
		Priority:                strconv.Itoa(cliCtx.Int("priority")),
		RuleStatus:              ruleStatus,
		ID:                      cliCtx.String("id"),
		DestBucket:              cliCtx.String("remote-bucket"),
		Op:                      replication.AddOption,
		ReplicateDeleteMarkers:  dmReplicateStatus,
		ReplicateDeletes:        deleteReplicationStatus,
		ReplicaSync:             replicaSync,
		ExistingObjectReplicate: existingReplicationStatus,
	}
	fatalIf(client.SetReplication(ctx, &rcfg, opts), "Could not add replication rule")
	printMsg(replicateAddMessage{
		Op:  "add",
		URL: aliasedURL,
		ID:  opts.ID,
	})
	return nil
}
<|endoftext|>