TUN-5754: Allow ingress validate to take plaintext option

Ingress validate currently validates config from a file. This PR adds a
new --json/-j flag to provide the ingress/config data as a plaintext
command line argument.
This commit is contained in:
Sudarsan Reddy 2022-02-22 15:51:43 +00:00
parent 051b2cf352
commit 9909e9d63c
2 changed files with 77 additions and 5 deletions

View File

@ -1,6 +1,7 @@
package tunnel package tunnel
import ( import (
"encoding/json"
"fmt" "fmt"
"net/url" "net/url"
@ -12,6 +13,15 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
const ingressDataJSONFlagName = "json"
var ingressDataJSON = &cli.StringFlag{
Name: ingressDataJSONFlagName,
Aliases: []string{"j"},
Usage: `Accepts data in the form of json as an input rather than read from a file`,
EnvVars: []string{"TUNNEL_INGRESS_VALIDATE_JSON"},
}
func buildIngressSubcommand() *cli.Command { func buildIngressSubcommand() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "ingress", Name: "ingress",
@ -49,6 +59,7 @@ func buildValidateIngressCommand() *cli.Command {
Usage: "Validate the ingress configuration ", Usage: "Validate the ingress configuration ",
UsageText: "cloudflared tunnel [--config FILEPATH] ingress validate", UsageText: "cloudflared tunnel [--config FILEPATH] ingress validate",
Description: "Validates the configuration file, ensuring your ingress rules are OK.", Description: "Validates the configuration file, ensuring your ingress rules are OK.",
Flags: []cli.Flag{ingressDataJSON},
} }
} }
@ -69,12 +80,11 @@ func buildTestURLCommand() *cli.Command {
// validateIngressCommand check the syntax of the ingress rules in the cloudflared config file // validateIngressCommand check the syntax of the ingress rules in the cloudflared config file
func validateIngressCommand(c *cli.Context, warnings string) error { func validateIngressCommand(c *cli.Context, warnings string) error {
conf := config.GetConfiguration() conf, err := getConfiguration(c)
if conf.Source() == "" { if err != nil {
fmt.Println("No configuration file was found. Please create one, or use the --config flag to specify its filepath. You can use the help command to learn more about configuration files") return err
return nil
} }
fmt.Println("Validating rules from", conf.Source())
if _, err := ingress.ParseIngress(conf); err != nil { if _, err := ingress.ParseIngress(conf); err != nil {
return errors.Wrap(err, "Validation failed") return errors.Wrap(err, "Validation failed")
} }
@ -90,6 +100,22 @@ func validateIngressCommand(c *cli.Context, warnings string) error {
return nil return nil
} }
func getConfiguration(c *cli.Context) (*config.Configuration, error) {
var conf *config.Configuration
if c.IsSet(ingressDataJSONFlagName) {
ingressJSON := c.String(ingressDataJSONFlagName)
fmt.Println("Validating rules from cmdline flag --json")
err := json.Unmarshal([]byte(ingressJSON), &conf)
return conf, err
}
conf = config.GetConfiguration()
if conf.Source() == "" {
return nil, errors.New("No configuration file was found. Please create one, or use the --config flag to specify its filepath. You can use the help command to learn more about configuration files")
}
fmt.Println("Validating rules from", conf.Source())
return conf, nil
}
// testURLCommand checks which ingress rule matches the given URL. // testURLCommand checks which ingress rule matches the given URL.
func testURLCommand(c *cli.Context) error { func testURLCommand(c *cli.Context) error {
requestArg := c.Args().First() requestArg := c.Args().First()

View File

@ -11,6 +11,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/cloudflare/cloudflared/cfapi" "github.com/cloudflare/cloudflared/cfapi"
@ -322,3 +323,48 @@ func Test_subcommandContext_Delete(t *testing.T) {
}) })
} }
} }
func Test_subcommandContext_ValidateIngressCommand(t *testing.T) {
var tests = []struct {
name string
c *cli.Context
wantErr bool
expectedErr error
}{
{
name: "read a valid configuration from data",
c: func() *cli.Context {
data := `{ "warp-routing": {"enabled": true}, "originRequest" : {"connectTimeout": 10}, "ingress" : [ {"hostname": "test", "service": "https://localhost:8000" } , {"service": "http_status:404"} ]}`
flagSet := flag.NewFlagSet("json", flag.PanicOnError)
flagSet.String(ingressDataJSONFlagName, data, "")
c := cli.NewContext(cli.NewApp(), flagSet, nil)
_ = c.Set(ingressDataJSONFlagName, data)
return c
}(),
},
{
name: "read an invalid configuration with multiple mistakes",
c: func() *cli.Context {
data := `{ "ingress" : [ {"hostname": "test", "service": "localhost:8000" } , {"service": "http_status:invalid_status"} ]}`
flagSet := flag.NewFlagSet("json", flag.PanicOnError)
flagSet.String(ingressDataJSONFlagName, data, "")
c := cli.NewContext(cli.NewApp(), flagSet, nil)
_ = c.Set(ingressDataJSONFlagName, data)
return c
}(),
wantErr: true,
expectedErr: errors.New("Validation failed: localhost:8000 is an invalid address, please make sure it has a scheme and a hostname"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateIngressCommand(tt.c, "")
if tt.wantErr {
assert.Equal(t, tt.expectedErr.Error(), err.Error())
} else {
assert.Nil(t, err)
}
})
}
}