Skip to content

Commit

Permalink
flagext: Add new type URLEscaped
Browse files Browse the repository at this point in the history
Sometimes we need to store just escaped URL in the config like S3 URL that contains credentials which can include characters like
`:` and `/`. Currently we cannot use normal `flagext.URLValue` as it just uses unescaped URL.

Please check issue
grafana/loki#1607
  • Loading branch information
kavirajk committed Feb 7, 2023
1 parent 6043e86 commit 6b8c28b
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 0 deletions.
63 changes: 63 additions & 0 deletions flagext/urlescaped.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package flagext

import "net/url"

// URLEscaped is a url.URL that can be used as a flag.
// URL value it contains will always be URL escaped and safe.
type URLEscaped struct {
*url.URL
}

// String implements flag.Value
func (v URLEscaped) String() string {
if v.URL == nil {
return ""
}
return v.URL.String()
}

// Set implements flag.Value
// Set make sure given URL string is escaped.
func (v *URLEscaped) Set(s string) error {
s = url.QueryEscape(s)

u, err := url.Parse(s)
if err != nil {
return err
}
v.URL = u
return nil
}

// UnmarshalYAML implements yaml.Unmarshaler.
func (v *URLEscaped) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
if err := unmarshal(&s); err != nil {
return err
}

// An empty string means no URL has been configured.
if s == "" {
v.URL = nil
return nil
}

return v.Set(s)
}

// Marshalyaml Implements yaml.Marshaler.
func (v URLEscaped) MarshalYAML() (interface{}, error) {
if v.URL == nil {
return "", nil
}

// Mask out passwords when marshalling URLs back to YAML.
u := *v.URL
if u.User != nil {
if _, set := u.User.Password(); set {
u.User = url.UserPassword(u.User.Username(), "********")
}
}

return u.String(), nil
}
38 changes: 38 additions & 0 deletions flagext/urlescaped_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package flagext

import (
"flag"
"fmt"
"net/url"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)

const (
testS3URL = "s3://ASDFGHJIQWETTYUI:Jkasduahdkjh213kj1h31+lkjaflkjzvKASDOasofhjafaKFAF/GoQd@region/bucket_name"
)

func TestURLEscaped(t *testing.T) {
expected, err := url.Parse(url.QueryEscape(testS3URL))
require.NoError(t, err)

// flag
var v URLEscaped
flags := flag.NewFlagSet("test", flag.ExitOnError)
flags.Var(&v, "v", "some secret credentials")
err = flags.Parse([]string{"-v", testS3URL})

assert.Equal(t, v.String(), expected.String())

// yaml
yv := struct {
S3 URLEscaped `yaml:"s3"`
}{}
err = yaml.Unmarshal([]byte(fmt.Sprintf("s3: %s", testS3URL)), &yv)
assert.NoError(t, err)
assert.Equal(t, yv.S3.String(), expected.String())

}

0 comments on commit 6b8c28b

Please sign in to comment.