cli/connhelper/ssh: use stdlib errors and improve errors

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-02-04 11:55:13 +01:00
parent 6ca9766897
commit 8c0c1db679
2 changed files with 44 additions and 14 deletions

View File

@ -2,9 +2,9 @@
package ssh
import (
"errors"
"fmt"
"net/url"
"github.com/pkg/errors"
)
// ParseURL creates a [Spec] from the given ssh URL. It returns an error if
@ -13,10 +13,25 @@ import (
func ParseURL(daemonURL string) (*Spec, error) {
u, err := url.Parse(daemonURL)
if err != nil {
return nil, err
var urlErr *url.Error
if errors.As(err, &urlErr) {
err = urlErr.Unwrap()
}
return nil, fmt.Errorf("invalid ssh URL: %w", err)
}
s, err := newSpec(u)
if err != nil {
return nil, fmt.Errorf("invalid ssh URL: %w", err)
}
return s, nil
}
func newSpec(u *url.URL) (*Spec, error) {
if u.Scheme == "" {
return nil, errors.New("no scheme provided")
}
if u.Scheme != "ssh" {
return nil, errors.Errorf("expected scheme ssh, got %q", u.Scheme)
return nil, errors.New("incorrect scheme: " + u.Scheme)
}
var sp Spec
@ -29,17 +44,18 @@ func ParseURL(daemonURL string) (*Spec, error) {
}
sp.Host = u.Hostname()
if sp.Host == "" {
return nil, errors.Errorf("no host specified")
return nil, errors.New("hostname is empty")
}
sp.Port = u.Port()
sp.Path = u.Path
if u.RawQuery != "" {
return nil, errors.Errorf("extra query after the host: %q", u.RawQuery)
return nil, fmt.Errorf("query parameters are not allowed: %q", u.RawQuery)
}
if u.Fragment != "" {
return nil, errors.Errorf("extra fragment after the host: %q", u.Fragment)
return nil, fmt.Errorf("fragments are not allowed: %q", u.Fragment)
}
return &sp, err
return &sp, nil
}
// Spec of SSH URL

View File

@ -84,30 +84,44 @@ func TestParseURL(t *testing.T) {
Path: "/var/run/docker.sock",
},
},
{
doc: "malformed URL",
url: "malformed %%url",
expectedError: `invalid ssh URL: invalid URL escape "%%u"`,
},
{
doc: "URL missing scheme",
url: "no-scheme.example.com",
expectedError: "invalid ssh URL: no scheme provided",
},
{
doc: "invalid URL with password",
url: "ssh://me:passw0rd@example.com",
expectedError: "plain-text password is not supported",
expectedError: "invalid ssh URL: plain-text password is not supported",
},
{
doc: "invalid URL with query parameter",
url: "ssh://example.com?bar",
expectedError: `extra query after the host: "bar"`,
url: "ssh://example.com?foo=bar&bar=baz",
expectedError: `invalid ssh URL: query parameters are not allowed: "foo=bar&bar=baz"`,
},
{
doc: "invalid URL with fragment",
url: "ssh://example.com#bar",
expectedError: `extra fragment after the host: "bar"`,
expectedError: `invalid ssh URL: fragments are not allowed: "bar"`,
},
{
doc: "invalid URL without hostname",
url: "ssh://",
expectedError: "no host specified",
expectedError: "invalid ssh URL: hostname is empty",
},
{
url: "ssh:///no-hostname",
expectedError: "invalid ssh URL: hostname is empty",
},
{
doc: "invalid URL with unsupported scheme",
url: "https://example.com",
expectedError: `expected scheme ssh, got "https"`,
expectedError: `invalid ssh URL: incorrect scheme: https`,
},
}
for _, tc := range testCases {