mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
refactor: embed install.sh script in uc CLI to not curl | sh and version together with CLI
This commit is contained in:
+16
-17
@@ -2,6 +2,7 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -13,14 +14,11 @@ import (
|
|||||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||||
"github.com/psviderski/uncloud/internal/sshexec"
|
"github.com/psviderski/uncloud/internal/sshexec"
|
||||||
|
"github.com/psviderski/uncloud/scripts"
|
||||||
"google.golang.org/protobuf/types/known/emptypb"
|
"google.golang.org/protobuf/types/known/emptypb"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const rootUser = "root"
|
||||||
// TODO: support pinning the script version to the CLI version.
|
|
||||||
installScriptURL = "https://raw.githubusercontent.com/psviderski/uncloud/refs/heads/main/scripts/install.sh"
|
|
||||||
rootUser = "root"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RemoteMachine struct {
|
type RemoteMachine struct {
|
||||||
User string
|
User string
|
||||||
@@ -30,26 +28,30 @@ type RemoteMachine struct {
|
|||||||
UseSSHGo bool // Use Go's built-in SSH library instead of the system ssh CLI command.
|
UseSSHGo bool // Use Go's built-in SSH library instead of the system ssh CLI command.
|
||||||
}
|
}
|
||||||
|
|
||||||
func installCmd(user string, version string) string {
|
// installCmd returns a shell command that decodes the base64-encoded install script and pipes it
|
||||||
|
// into bash, optionally via sudo and with UNCLOUD_* environment variables set.
|
||||||
|
func installCmd(scriptBase64, user, version string) string {
|
||||||
sudoPrefix := ""
|
sudoPrefix := ""
|
||||||
var env []string
|
var env []string
|
||||||
|
|
||||||
// Add the SSH user (non-root) to the uncloud group to allow access to the Uncloud daemon unix socket.
|
// Add the SSH user (non-root) to the uncloud group to allow access to the Uncloud daemon unix socket.
|
||||||
if user != rootUser {
|
if user != rootUser {
|
||||||
sudoPrefix = "sudo"
|
sudoPrefix = "sudo "
|
||||||
env = append(env, "UNCLOUD_GROUP_ADD_USER="+sshexec.Quote(user))
|
env = append(env, "UNCLOUD_GROUP_ADD_USER="+sshexec.Quote(user))
|
||||||
}
|
}
|
||||||
if version != "" {
|
if version != "" {
|
||||||
env = append(env, "UNCLOUD_VERSION="+sshexec.Quote(version))
|
env = append(env, "UNCLOUD_VERSION="+sshexec.Quote(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
envCmd := strings.Join(env, " ")
|
envPrefix := ""
|
||||||
curlBashCmd := fmt.Sprintf("curl -fsSL %s | %s %s bash", sshexec.Quote(installScriptURL), sudoPrefix, envCmd)
|
if len(env) > 0 {
|
||||||
|
envPrefix = strings.Join(env, " ") + " "
|
||||||
|
}
|
||||||
|
|
||||||
return curlBashCmd
|
return fmt.Sprintf("printf '%%s' %s | base64 -d | %s%sbash", scriptBase64, sudoPrefix, envPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// provisionMachine provisions the remote machine by downloading the Uncloud install script from GitHub and running it.
|
// provisionMachine provisions the remote machine by running the Uncloud install script embedded in the uc CLI.
|
||||||
// If version is specified, it will be passed to the install script as UNCLOUD_VERSION environment variable.
|
// If version is specified, it will be passed to the install script as UNCLOUD_VERSION environment variable.
|
||||||
func provisionMachine(ctx context.Context, exec sshexec.Executor, version string) error {
|
func provisionMachine(ctx context.Context, exec sshexec.Executor, version string) error {
|
||||||
user, err := exec.Run(ctx, "whoami")
|
user, err := exec.Run(ctx, "whoami")
|
||||||
@@ -88,13 +90,10 @@ func provisionMachine(ctx context.Context, exec sshexec.Executor, version string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := installCmd(user, version)
|
scriptBase64 := base64.StdEncoding.EncodeToString([]byte(scripts.InstallScript))
|
||||||
|
cmd := sshexec.QuoteCommand("bash", "-c", "set -o pipefail; "+installCmd(scriptBase64, user, version))
|
||||||
fmt.Println("Downloading Uncloud install script:", installScriptURL)
|
|
||||||
|
|
||||||
cmd = sshexec.QuoteCommand("bash", "-c", "set -o pipefail; "+cmd)
|
|
||||||
if err = exec.Stream(ctx, cmd, os.Stdout, os.Stderr); err != nil {
|
if err = exec.Stream(ctx, cmd, os.Stdout, os.Stderr); err != nil {
|
||||||
return fmt.Errorf("download and run install script: %w", err)
|
return fmt.Errorf("run install script: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,30 +7,42 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestInstallCmd(t *testing.T) {
|
func TestInstallCmd(t *testing.T) {
|
||||||
t.Run("root", func(t *testing.T) {
|
const scriptB64 = "SCRIPT_BASE64_PLACEHOLDER"
|
||||||
cmd := installCmd("root", "")
|
|
||||||
assert.NotContains(t, cmd, "sudo")
|
|
||||||
assert.NotContains(t, cmd, "UNCLOUD_GROUP_ADD_USER")
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test with version
|
tests := []struct {
|
||||||
t.Run("root with version", func(t *testing.T) {
|
name string
|
||||||
cmd := installCmd("root", "v1.2.3")
|
user string
|
||||||
assert.NotContains(t, cmd, "sudo")
|
version string
|
||||||
assert.NotContains(t, cmd, "UNCLOUD_GROUP_ADD_USER")
|
want string
|
||||||
assert.Contains(t, cmd, "UNCLOUD_VERSION=v1.2.3")
|
}{
|
||||||
})
|
{
|
||||||
|
name: "root",
|
||||||
|
user: "root",
|
||||||
|
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | bash",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "root with version",
|
||||||
|
user: "root",
|
||||||
|
version: "v1.2.3",
|
||||||
|
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | UNCLOUD_VERSION=v1.2.3 bash",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nonroot",
|
||||||
|
user: "nonroot",
|
||||||
|
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | sudo UNCLOUD_GROUP_ADD_USER=nonroot bash",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nonroot with version",
|
||||||
|
user: "nonroot",
|
||||||
|
version: "v1.2.3",
|
||||||
|
want: "printf '%s' SCRIPT_BASE64_PLACEHOLDER | base64 -d | " +
|
||||||
|
"sudo UNCLOUD_GROUP_ADD_USER=nonroot UNCLOUD_VERSION=v1.2.3 bash",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("nonroot", func(t *testing.T) {
|
for _, tt := range tests {
|
||||||
cmd := installCmd("nonroot", "")
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
assert.Contains(t, cmd, "sudo")
|
assert.Equal(t, tt.want, installCmd(scriptB64, tt.user, tt.version))
|
||||||
assert.Contains(t, cmd, "UNCLOUD_GROUP_ADD_USER=nonroot")
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
t.Run("nonroot with version", func(t *testing.T) {
|
|
||||||
cmd := installCmd("nonroot", "v1.2.3")
|
|
||||||
assert.Contains(t, cmd, "sudo")
|
|
||||||
assert.Contains(t, cmd, "UNCLOUD_GROUP_ADD_USER=nonroot")
|
|
||||||
assert.Contains(t, cmd, "UNCLOUD_VERSION=v1.2.3")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// Package scripts embeds shell scripts shipped with the uc CLI so they can be
|
||||||
|
// executed on remote machines without being fetched from the network.
|
||||||
|
package scripts
|
||||||
|
|
||||||
|
import _ "embed"
|
||||||
|
|
||||||
|
//go:embed install.sh
|
||||||
|
var InstallScript string
|
||||||
Reference in New Issue
Block a user