TUN-3463: Let users run a named tunnel via config file setting
This commit is contained in:
parent
acd03e36e6
commit
051908aaef
|
@ -10,9 +10,11 @@ import (
|
||||||
|
|
||||||
homedir "github.com/mitchellh/go-homedir"
|
homedir "github.com/mitchellh/go-homedir"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
"github.com/urfave/cli/v2/altsrc"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"github.com/cloudflare/cloudflared/ingress"
|
"github.com/cloudflare/cloudflared/ingress"
|
||||||
|
"github.com/cloudflare/cloudflared/logger"
|
||||||
"github.com/cloudflare/cloudflared/validation"
|
"github.com/cloudflare/cloudflared/validation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -199,7 +201,7 @@ func ValidateUrl(c *cli.Context, allowFromArgs bool) (string, error) {
|
||||||
func ReadRules(c *cli.Context) (ingress.Ingress, error) {
|
func ReadRules(c *cli.Context) (ingress.Ingress, error) {
|
||||||
configFilePath := c.String("config")
|
configFilePath := c.String("config")
|
||||||
if configFilePath == "" {
|
if configFilePath == "" {
|
||||||
return ingress.Ingress{}, ErrNoConfigFile
|
return ingress.Ingress{}, ingress.ErrNoIngressRules
|
||||||
}
|
}
|
||||||
fmt.Printf("Reading from config file %s\n", configFilePath)
|
fmt.Printf("Reading from config file %s\n", configFilePath)
|
||||||
configBytes, err := ioutil.ReadFile(configFilePath)
|
configBytes, err := ioutil.ReadFile(configFilePath)
|
||||||
|
@ -209,3 +211,31 @@ func ReadRules(c *cli.Context) (ingress.Ingress, error) {
|
||||||
rules, err := ingress.ParseIngress(configBytes)
|
rules, err := ingress.ParseIngress(configBytes)
|
||||||
return rules, err
|
return rules, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var configFileInputSource struct {
|
||||||
|
lastLoadedFile string
|
||||||
|
context altsrc.InputSourceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfigFileSource returns InputSourceContext initialized from the configuration file.
|
||||||
|
// On repeat calls returns with the same file, returns without reading the file again; however,
|
||||||
|
// if value of "config" flag changes, will read the new config file
|
||||||
|
func GetConfigFileSource(c *cli.Context, log logger.Service) (altsrc.InputSourceContext, error) {
|
||||||
|
configFile := c.String("config")
|
||||||
|
if configFileInputSource.lastLoadedFile == configFile {
|
||||||
|
if configFileInputSource.context == nil {
|
||||||
|
return nil, ErrNoConfigFile
|
||||||
|
}
|
||||||
|
return configFileInputSource.context, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
configFileInputSource.lastLoadedFile = configFile
|
||||||
|
log.Debugf("Loading configuration from %s", configFile)
|
||||||
|
src, err := altsrc.NewYamlSourceFromFile(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
configFileInputSource.context = src
|
||||||
|
return src, nil
|
||||||
|
}
|
||||||
|
|
|
@ -199,24 +199,29 @@ func buildIngressSubcommand() *cli.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TunnelCommand(c *cli.Context) error {
|
func TunnelCommand(c *cli.Context) error {
|
||||||
if name := c.String("name"); name != "" { // Start a named tunnel
|
sc, err := newSubcommandContext(c)
|
||||||
return adhocNamedTunnel(c, name)
|
if err != nil {
|
||||||
} else { // Start a classic tunnel
|
return err
|
||||||
return classicTunnel(c)
|
|
||||||
}
|
}
|
||||||
|
if name := c.String("name"); name != "" { // Start a named tunnel
|
||||||
|
return runAdhocNamedTunnel(sc, name)
|
||||||
|
}
|
||||||
|
if ref, err := sc.getConfigFileTunnelRef(); err != nil {
|
||||||
|
return err
|
||||||
|
} else if ref != "" {
|
||||||
|
return runNamedTunnel(sc, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a classic tunnel
|
||||||
|
return runClassicTunnel(sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(v string, s, g chan struct{}) {
|
func Init(v string, s, g chan struct{}) {
|
||||||
version, shutdownC, graceShutdownC = v, s, g
|
version, shutdownC, graceShutdownC = v, s, g
|
||||||
}
|
}
|
||||||
|
|
||||||
// adhocNamedTunnel create, route and run a named tunnel in one command
|
// runAdhocNamedTunnel create, route and run a named tunnel in one command
|
||||||
func adhocNamedTunnel(c *cli.Context, name string) error {
|
func runAdhocNamedTunnel(sc *subcommandContext, name string) error {
|
||||||
sc, err := newSubcommandContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tunnel, ok, err := sc.tunnelActive(name)
|
tunnel, ok, err := sc.tunnelActive(name)
|
||||||
if err != nil || !ok {
|
if err != nil || !ok {
|
||||||
tunnel, err = sc.create(name)
|
tunnel, err = sc.create(name)
|
||||||
|
@ -227,7 +232,7 @@ func adhocNamedTunnel(c *cli.Context, name string) error {
|
||||||
sc.logger.Infof("Tunnel already created with ID %s", tunnel.ID)
|
sc.logger.Infof("Tunnel already created with ID %s", tunnel.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r, ok := routeFromFlag(c); ok {
|
if r, ok := routeFromFlag(sc.c); ok {
|
||||||
if res, err := sc.route(tunnel.ID, r); err != nil {
|
if res, err := sc.route(tunnel.ID, r); err != nil {
|
||||||
sc.logger.Errorf("failed to create route, please create it manually. err: %v.", err)
|
sc.logger.Errorf("failed to create route, please create it manually. err: %v.", err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -242,14 +247,9 @@ func adhocNamedTunnel(c *cli.Context, name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// classicTunnel creates a "classic" non-named tunnel
|
// runClassicTunnel creates a "classic" non-named tunnel
|
||||||
func classicTunnel(c *cli.Context) error {
|
func runClassicTunnel(sc *subcommandContext) error {
|
||||||
sc, err := newSubcommandContext(c)
|
return StartServer(sc.c, version, shutdownC, graceShutdownC, nil, sc.logger, sc.isUIEnabled)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return StartServer(c, version, shutdownC, graceShutdownC, nil, sc.logger, sc.isUIEnabled)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func routeFromFlag(c *cli.Context) (tunnelstore.Route, bool) {
|
func routeFromFlag(c *cli.Context) (tunnelstore.Route, bool) {
|
||||||
|
@ -571,34 +571,26 @@ func forceSetFlag(c *cli.Context, name, value string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetFlagsFromConfigFile(c *cli.Context) error {
|
func SetFlagsFromConfigFile(c *cli.Context) error {
|
||||||
logger, err := createLogger(c, false, false)
|
log, err := createLogger(c, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cliutil.PrintLoggerSetupError("error setting up logger", err)
|
return cliutil.PrintLoggerSetupError("error setting up logger", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
configFile := c.String("config")
|
inputSource, err := config.GetConfigFileSource(c, log)
|
||||||
if configFile == "" {
|
if err != nil {
|
||||||
logger.Debugf(config.ErrNoConfigFile.Error())
|
if err == config.ErrNoConfigFile {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
inputSource, err := altsrc.NewYamlSourceFromFile(configFile)
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("Cannot load configuration from %s: %s", configFile, err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if inputSource != nil {
|
|
||||||
targetFlags := c.Command.Flags
|
targetFlags := c.Command.Flags
|
||||||
if c.Command.Name == "" {
|
if c.Command.Name == "" {
|
||||||
targetFlags = c.App.Flags
|
targetFlags = c.App.Flags
|
||||||
}
|
}
|
||||||
err := altsrc.ApplyInputSourceValues(c, inputSource, targetFlags)
|
if err := altsrc.ApplyInputSourceValues(c, inputSource, targetFlags); err != nil {
|
||||||
if err != nil {
|
log.Errorf("Cannot load configuration from %s: %v", inputSource.Source(), err)
|
||||||
logger.Errorf("Cannot apply configuration from %s: %s", configFile, err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Debugf("Applied configuration from %s", configFile)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ func dnsProxyStandAlone(c *cli.Context) bool {
|
||||||
func findOriginCert(c *cli.Context, logger logger.Service) (string, error) {
|
func findOriginCert(c *cli.Context, logger logger.Service) (string, error) {
|
||||||
originCertPath := c.String("origincert")
|
originCertPath := c.String("origincert")
|
||||||
if originCertPath == "" {
|
if originCertPath == "" {
|
||||||
logger.Infof(config.ErrNoConfigFile.Error())
|
logger.Infof("Cannot determine default origin certificate path. No file %s in %v", config.DefaultCredentialFile, config.DefaultConfigSearchDirectories())
|
||||||
if isRunningFromTerminal() {
|
if isRunningFromTerminal() {
|
||||||
logger.Errorf("You need to specify the origin certificate path with --origincert option, or set TUNNEL_ORIGIN_CERT environment variable. See %s for more information.", argumentsUrl)
|
logger.Errorf("You need to specify the origin certificate path with --origincert option, or set TUNNEL_ORIGIN_CERT environment variable. See %s for more information.", argumentsUrl)
|
||||||
return "", fmt.Errorf("Client didn't specify origincert path when running from terminal")
|
return "", fmt.Errorf("Client didn't specify origincert path when running from terminal")
|
||||||
|
|
|
@ -8,15 +8,16 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"github.com/cloudflare/cloudflared/certutil"
|
"github.com/cloudflare/cloudflared/certutil"
|
||||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
|
||||||
"github.com/cloudflare/cloudflared/logger"
|
"github.com/cloudflare/cloudflared/logger"
|
||||||
"github.com/cloudflare/cloudflared/origin"
|
"github.com/cloudflare/cloudflared/origin"
|
||||||
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||||
"github.com/cloudflare/cloudflared/tunnelstore"
|
"github.com/cloudflare/cloudflared/tunnelstore"
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// subcommandContext carries structs shared between subcommands, to reduce number of arguments needed to
|
// subcommandContext carries structs shared between subcommands, to reduce number of arguments needed to
|
||||||
|
@ -143,6 +144,18 @@ func (sc *subcommandContext) tunnelCredentialsPath(tunnelID uuid.UUID) (string,
|
||||||
return "", fmt.Errorf("Tunnel credentials file not found")
|
return "", fmt.Errorf("Tunnel credentials file not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getConfigFileTunnelRef returns tunnel UUID or name set in the configuration file
|
||||||
|
func (sc *subcommandContext) getConfigFileTunnelRef() (string, error) {
|
||||||
|
if src, err := config.GetConfigFileSource(sc.c, sc.logger); err == nil {
|
||||||
|
if tunnelRef, err := src.String("tunnel"); err != nil {
|
||||||
|
return "", errors.Wrapf(err, "invalid tunnel ID or name")
|
||||||
|
} else {
|
||||||
|
return tunnelRef, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
func (sc *subcommandContext) create(name string) (*tunnelstore.Tunnel, error) {
|
func (sc *subcommandContext) create(name string) (*tunnelstore.Tunnel, error) {
|
||||||
client, err := sc.client()
|
client, err := sc.client()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -315,9 +315,10 @@ func buildRunCommand() *cli.Command {
|
||||||
Action: cliutil.ErrorHandler(runCommand),
|
Action: cliutil.ErrorHandler(runCommand),
|
||||||
Before: SetFlagsFromConfigFile,
|
Before: SetFlagsFromConfigFile,
|
||||||
Usage: "Proxy a local web server by running the given tunnel",
|
Usage: "Proxy a local web server by running the given tunnel",
|
||||||
UsageText: "cloudflared tunnel [tunnel command options] run [subcommand options] TUNNEL",
|
UsageText: "cloudflared tunnel [tunnel command options] run [subcommand options] [TUNNEL]",
|
||||||
Description: `Runs the tunnel identified by name or UUUD, creating highly available connections
|
Description: `Runs the tunnel identified by name or UUUD, creating highly available connections
|
||||||
between your server and the Cloudflare edge.
|
between your server and the Cloudflare edge. You can provide name or UUID of tunnel to run either as the
|
||||||
|
last command line argument or in the configuration file using "tunnel: TUNNEL".
|
||||||
|
|
||||||
This command requires the tunnel credentials file created when "cloudflared tunnel create" was run,
|
This command requires the tunnel credentials file created when "cloudflared tunnel create" was run,
|
||||||
however it does not need access to cert.pem from "cloudflared login" if you identify the tunnel by UUID.
|
however it does not need access to cert.pem from "cloudflared login" if you identify the tunnel by UUID.
|
||||||
|
@ -335,14 +336,32 @@ func runCommand(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.NArg() != 1 {
|
if c.NArg() > 1 {
|
||||||
return cliutil.UsageError(`"cloudflared tunnel run" requires exactly 1 argument, the ID or name of the tunnel to run.`)
|
return cliutil.UsageError(`"cloudflared tunnel run" accepts only one argument, the ID or name of the tunnel to run.`)
|
||||||
}
|
}
|
||||||
tunnelID, err := sc.findID(c.Args().First())
|
tunnelRef := c.Args().First()
|
||||||
|
if tunnelRef == "" {
|
||||||
|
// attempt to read from the config file
|
||||||
|
if tunnelRef, err = sc.getConfigFileTunnelRef(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tunnelRef == "" {
|
||||||
|
return cliutil.UsageError(`"cloudflared tunnel run" requires the ID or name of the tunnel to run as the last command line argument or in the configuration file.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return runNamedTunnel(sc, tunnelRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runNamedTunnel(sc *subcommandContext, tunnelRef string) error {
|
||||||
|
tunnelID, err := sc.findID(tunnelRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "error parsing tunnel ID")
|
return errors.Wrap(err, "error parsing tunnel ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sc.logger.Infof("Starting tunnel %s", tunnelID.String())
|
||||||
|
|
||||||
return sc.run(tunnelID)
|
return sc.run(tunnelID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue