Repository: hairyhenderson/gomplate (go)

Issue:
Secrets Manager Datasource Doesn't Appear to Work
```
./gomplate --version
gomplate version 4.1.0

aws --version
aws-cli/1.24.10 Python/3.6.8 Linux/4.18.0-553.8.1.el8_10.x86_64 botocore/1.26.10

cat /etc/redhat-release
Red Hat Enterprise Linux release 8.10 (Ootpa)
```

Hi,

I'm attempting to retrieve secrets using AWS Secrets Manager (on an EC2 instance), however, I'm getting this error:
```
ec2-user]# ./gomplate --verbose -d 'foo=aws+sm:' -i '{{ ds "foo" "goemplate" }}'
13:32:18 DBG starting gomplate
13:32:18 DBG config is:
---
datasources:
  foo:
    header: {}
    url: 'aws+sm:'
in: '{{ ds "f...'
 version=4.1.0 build=cc2584028866967a39b096265d5b9af4516c734f
13:32:18 DBG completed rendering templatesRendered=0 errors=1 duration=84.088165ms
13:32:18 ERR  err="renderTemplate: failed to render template <arg>: template: <arg>:1:3: executing \"<arg>\" at <ds \"foo\" \"goemplate\">: error calling ds: couldn't read datasource 'foo' (aws+sm:///goemplate): stat (url: \"aws+sm:///\", name: \"goemplate\"): stat goemplate: file does not exist"
```

Looking at my CloudTrail logs, I'm seeing this:
```
...
errorCode	ResourceNotFoundException
errorMessage	Secrets Manager can't find the specified secret.
eventCategory	Management
eventID	NNee72f2-f237-4467-81b4-fe0d81f8cNNN
eventName	GetSecretValue
eventSource	secretsmanager.amazonaws.com
eventTime	2024-07-23T13:23:37Z
eventType	AwsApiCall
eventVersion	1.09
managementEvent	1
readOnly	1
requestParameters.secretId	/goemplate
...
```

As you can see on that last line, a forward slash is being included at the start of the secretId when it shouldn't be. This also occurs if I specify, for instance, goemplate/something or goemplate/something/something.

Any assistance appreciated and thanks for a great tool.

Pull request description:
fix(datasources): Fix aws+sm bug when reading secrets not starting with slash
Fixes #2190 

Patch:
diff --git a/internal/datafs/fsurl.go b/internal/datafs/fsurl.go
index a1cf5cbc..f34324a0 100644
--- a/internal/datafs/fsurl.go
+++ b/internal/datafs/fsurl.go
@@ -21,6 +21,15 @@ func SplitFSMuxURL(in *url.URL) (*url.URL, string) {
 		}
 
 		return &u, base
+	case "aws+sm":
+		// An aws+sm URL can either be opaque or have a path with a leading
+		// slash. If it's opaque, the URL must not contain a leading slash. If
+		// it has a path, the URL must begin with a slash.
+		if u.Opaque != "" {
+			return &url.URL{Scheme: u.Scheme}, u.Opaque
+		} else {
+			return &url.URL{Scheme: u.Scheme, Path: "/"}, strings.TrimLeft(u.Path, "/")
+		}
 	}
 
 	// trim leading and trailing slashes - they are not part of a valid path
diff --git a/internal/datafs/fsys.go b/internal/datafs/fsys.go
index 03abdf6d..1102fb8a 100644
--- a/internal/datafs/fsys.go
+++ b/internal/datafs/fsys.go
@@ -55,6 +55,8 @@ func FSysForPath(ctx context.Context, path string) (fs.FS, error) {
 	switch u.Scheme {
 	case "git+http", "git+https", "git+ssh", "git":
 		// no-op, these are handled
+	case "aws+sm":
+		// An aws+sm URL can be opaque, best not disturb it
 	case "", "file", "git+file":
 		// default to "/" so we have a rooted filesystem for all schemes, but also
 		// support volumes on Windows
diff --git a/internal/datafs/reader.go b/internal/datafs/reader.go
index 68d5d37f..52ebefe1 100644
--- a/internal/datafs/reader.go
+++ b/internal/datafs/reader.go
@@ -122,13 +122,13 @@ func (d *dsReader) readFileContent(ctx context.Context, u *url.URL, hdr http.Hea
 	// leaking into the filesystem layer
 	u = removeQueryParam(u, overrideType)
 
+	u, fname := SplitFSMuxURL(u)
+
 	fsys, err := FSysForPath(ctx, u.String())
 	if err != nil {
 		return nil, fmt.Errorf("fsys for path %v: %w", u, err)
 	}
 
-	u, fname := SplitFSMuxURL(u)
-
 	// need to support absolute paths on local filesystem too
 	// TODO: this is a hack, probably fix this?
 	if u.Scheme == "file" && runtime.GOOS != "windows" {


Test patch:
diff --git a/internal/datafs/fsurl_test.go b/internal/datafs/fsurl_test.go
index 00bba69b..bcfa3c86 100644
--- a/internal/datafs/fsurl_test.go
+++ b/internal/datafs/fsurl_test.go
@@ -95,6 +95,26 @@ func TestSplitFSMuxURL(t *testing.T) {
 			"merge:///",
 			"vault:///foo/bar|foo|git+ssh://git@github.com/hairyhenderson/go-which.git//a/b/c/d",
 		},
+		{
+			"aws+sm:foo",
+			"aws+sm:",
+			"foo",
+		},
+		{
+			"aws+sm:foo/bar",
+			"aws+sm:",
+			"foo/bar",
+		},
+		{
+			"aws+sm:/foo/bar",
+			"aws+sm:///",
+			"foo/bar",
+		},
+		{
+			"aws+sm:/foo",
+			"aws+sm:///",
+			"foo",
+		},
 	}
 
 	for _, d := range testdata {