Skip to content

Commit

Permalink
test(e2e): add ssh key e2e tests (#947)
Browse files Browse the repository at this point in the history
  • Loading branch information
phm07 authored Jan 3, 2025
1 parent b3d1238 commit 0998186
Show file tree
Hide file tree
Showing 2 changed files with 202 additions and 1 deletion.
39 changes: 38 additions & 1 deletion test/e2e/combined_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,35 @@ package e2e

import (
"fmt"
"os"
"path"
"strconv"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/hetznercloud/hcloud-go/v2/hcloud/exp/kit/sshutil"
)

func TestCombined(t *testing.T) {
// combined tests combine multiple resources and can thus not be run in parallel
priv, pub, err := sshutil.GenerateKeyPair()
require.NoError(t, err)

keyDir := t.TempDir()
pubKeyPath, privKeyPath := path.Join(keyDir, "id_ed25519.pub"), path.Join(keyDir, "id_ed25519")
err = os.WriteFile(privKeyPath, priv, 0600)
require.NoError(t, err)
err = os.WriteFile(pubKeyPath, pub, 0644)
require.NoError(t, err)

sshKeyName := withSuffix("test-ssh-key")
sshKeyID, err := createSSHKey(t, sshKeyName, "--public-key-from-file", pubKeyPath)
require.NoError(t, err)

serverName := withSuffix("test-server")
serverID, err := createServer(t, serverName, TestServerType, TestImage)
serverID, err := createServer(t, serverName, TestServerType, TestImage, "--ssh-key", strconv.FormatInt(sshKeyID, 10))
require.NoError(t, err)

firewallName := withSuffix("test-firewall")
Expand Down Expand Up @@ -105,9 +123,28 @@ func TestCombined(t *testing.T) {
})
})

t.Run("ssh", func(t *testing.T) {
out, err := runCommand(
t, "server", "ssh", strconv.FormatInt(serverID, 10),
"-i", privKeyPath,
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "IdentitiesOnly=yes",
"--", "exit",
)
require.NoError(t, err)
assert.Empty(t, out)
})

t.Run("delete-server", func(t *testing.T) {
out, err := runCommand(t, "server", "delete", strconv.FormatInt(serverID, 10))
require.NoError(t, err)
assert.Equal(t, fmt.Sprintf("Server %d deleted\n", serverID), out)
})

t.Run("delete-ssh-key", func(t *testing.T) {
out, err := runCommand(t, "ssh-key", "delete", strconv.FormatInt(sshKeyID, 10))
require.NoError(t, err)
assert.Equal(t, fmt.Sprintf("SSH Key %d deleted\n", sshKeyID), out)
})
}
164 changes: 164 additions & 0 deletions test/e2e/sshkey_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
//go:build e2e

package e2e

import (
"context"
"fmt"
"strconv"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/swaggest/assertjson"

"github.com/hetznercloud/hcloud-go/v2/hcloud"
"github.com/hetznercloud/hcloud-go/v2/hcloud/exp/kit/sshutil"
)

func TestSSHKey(t *testing.T) {
t.Parallel()

pubKey, fingerprint, err := generateSSHKey()
require.NoError(t, err)

sshKeyName := withSuffix("test-ssh-key")
sshKeyID, err := createSSHKey(t, sshKeyName, "--public-key", pubKey)
require.NoError(t, err)

t.Run("add-label", func(t *testing.T) {
t.Run("non-existing", func(t *testing.T) {
out, err := runCommand(t, "ssh-key", "add-label", "non-existing-ssh-key", "foo=bar")
require.EqualError(t, err, "ssh key not found: non-existing-ssh-key")
assert.Empty(t, out)
})

t.Run("1", func(t *testing.T) {
out, err := runCommand(t, "ssh-key", "add-label", strconv.FormatInt(sshKeyID, 10), "foo=bar")
require.NoError(t, err)
assert.Equal(t, fmt.Sprintf("Label(s) foo added to SSH Key %d\n", sshKeyID), out)
})

t.Run("2", func(t *testing.T) {
out, err := runCommand(t, "ssh-key", "add-label", strconv.FormatInt(sshKeyID, 10), "baz=qux")
require.NoError(t, err)
assert.Equal(t, fmt.Sprintf("Label(s) baz added to SSH Key %d\n", sshKeyID), out)
})
})

t.Run("list", func(t *testing.T) {
t.Run("table", func(t *testing.T) {
out, err := runCommand(t, "ssh-key", "list", "-o=columns=id,name,fingerprint,public_key,labels,created,age")
require.NoError(t, err)
assert.Regexp(t,
NewRegex().Start().
SeparatedByWhitespace("ID", "NAME", "FINGERPRINT", "PUBLIC KEY", "LABELS", "CREATED", "AGE").Newline().
Lit(strconv.FormatInt(sshKeyID, 10)).Whitespace().
Lit(sshKeyName).Whitespace().
Lit(fingerprint).Whitespace().
Lit(pubKey).Whitespace().
Lit("baz=qux, foo=bar").Whitespace().
UnixDate().Whitespace().
Age().Newline().
End(),
out,
)
})

t.Run("json", func(t *testing.T) {
out, err := runCommand(t, "ssh-key", "list", "-o=json")
require.NoError(t, err)
assertjson.Equal(t, []byte(fmt.Sprintf(`
[
{
"id": %d,
"name": %q,
"fingerprint": %q,
"public_key": %q,
"labels": {
"baz": "qux",
"foo": "bar"
},
"created": "<ignore-diff>"
}
]`, sshKeyID, sshKeyName, fingerprint, pubKey)), []byte(out))
})
})

t.Run("remove-label", func(t *testing.T) {
out, err := runCommand(t, "ssh-key", "remove-label", strconv.FormatInt(sshKeyID, 10), "baz")
require.NoError(t, err)
assert.Equal(t, fmt.Sprintf("Label(s) baz removed from SSH Key %d\n", sshKeyID), out)
})

t.Run("update-name", func(t *testing.T) {
sshKeyName = withSuffix("new-test-ssh-key")
out, err := runCommand(t, "ssh-key", "update", strconv.FormatInt(sshKeyID, 10), "--name", sshKeyName)
require.NoError(t, err)
assert.Equal(t, fmt.Sprintf("SSHKey %d updated\n", sshKeyID), out)
})

t.Run("describe", func(t *testing.T) {
out, err := runCommand(t, "ssh-key", "describe", strconv.FormatInt(sshKeyID, 10))
require.NoError(t, err)
assert.Regexp(t, NewRegex().Start().
Lit("ID:").Whitespace().Lit(strconv.FormatInt(sshKeyID, 10)).Newline().
Lit("Name:").Whitespace().Lit(sshKeyName).Newline().
Lit("Created:").Whitespace().UnixDate().Lit(" (").HumanizeTime().Lit(")").Newline().
Lit("Fingerprint:").Whitespace().Lit(fingerprint).Newline().
Lit("Public Key:").Newline().Lit(pubKey).
Lit("Labels:").Newline().
Lit(" foo:").Whitespace().Lit("bar").Newline().
End(),
out,
)
})

t.Run("delete", func(t *testing.T) {
out, err := runCommand(t, "ssh-key", "delete", strconv.FormatInt(sshKeyID, 10))
require.NoError(t, err)
assert.Equal(t, fmt.Sprintf("SSH Key %d deleted\n", sshKeyID), out)
})
}

func createSSHKey(t *testing.T, name string, args ...string) (int64, error) {
t.Helper()
t.Cleanup(func() {
_, _ = client.SSHKey.Delete(context.Background(), &hcloud.SSHKey{Name: name})
})

out, err := runCommand(t, append([]string{"ssh-key", "create", "--name", name}, args...)...)
if err != nil {
return 0, err
}

if !assert.Regexp(t, `^SSH key [0-9]+ created\n$`, out) {
return 0, fmt.Errorf("invalid response: %s", out)
}

id, err := strconv.ParseInt(out[8:len(out)-9], 10, 64)
if err != nil {
return 0, err
}

t.Cleanup(func() {
_, _ = client.SSHKey.Delete(context.Background(), &hcloud.SSHKey{ID: id})
})
return id, nil
}

func generateSSHKey() (string, string, error) {
// ed25519 SSH key
_, pub, err := sshutil.GenerateKeyPair()
if err != nil {
return "", "", err
}

// MD5 fingerprint
fingerprint, err := sshutil.GetPublicKeyFingerprint(pub)
if err != nil {
return "", "", err
}

return string(pub), fingerprint, nil
}

0 comments on commit 0998186

Please sign in to comment.