Skip to content

Commit

Permalink
Add local install mode for k3sup install
Browse files Browse the repository at this point in the history
Closes: #2

This feature allows the k3sup binary to be used to install k3s
within cloud-init, through bash, or for general local usage
without needing to pass through ssh.

Tested against a Civo Ubuntu node both remotely and with an
IP given. The new flag is --local.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <[email protected]>
  • Loading branch information
alexellis committed Nov 25, 2019
1 parent f59225a commit ce08b1b
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 46 deletions.
107 changes: 74 additions & 33 deletions pkg/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"strings"

config "github.com/alexellis/k3sup/pkg/config"
kssh "github.com/alexellis/k3sup/pkg/ssh"
operator "github.com/alexellis/k3sup/pkg/operator"

homedir "github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
Expand All @@ -32,7 +32,7 @@ func MakeInstall() *cobra.Command {
SilenceUsage: true,
}

command.Flags().IP("ip", nil, "Public IP of node")
command.Flags().IP("ip", net.ParseIP("127.0.0.1"), "Public IP of node")
command.Flags().String("user", "root", "Username for SSH login")

command.Flags().String("ssh-key", "~/.ssh/id_rsa", "The ssh key to use for remote login")
Expand All @@ -45,6 +45,8 @@ func MakeInstall() *cobra.Command {
command.Flags().Bool("merge", false, "Merge the config with existing kubeconfig if it already exists.\nProvide the --local-path flag with --merge if a kubeconfig already exists in some other directory")
command.Flags().String("k3s-version", config.K3sVersion, "Optional version to install, pinned at a default")

command.Flags().Bool("local", false, "Perform a local install without using ssh")

command.RunE = func(command *cobra.Command, args []string) error {

localKubeconfig, _ := command.Flags().GetString("local-path")
Expand All @@ -57,18 +59,49 @@ func MakeInstall() *cobra.Command {
sudoPrefix = "sudo "
}

port, _ := command.Flags().GetInt("ssh-port")
k3sVersion, _ := command.Flags().GetString("k3s-version")
k3sExtraArgs, _ := command.Flags().GetString("k3s-extra-args")

local, _ := command.Flags().GetBool("local")

ip, _ := command.Flags().GetIP("ip")
fmt.Println("Public IP: " + ip.String())

user, _ := command.Flags().GetString("user")
sshKey, _ := command.Flags().GetString("ssh-key")
installK3scommand := fmt.Sprintf("curl -sLS https://get.k3s.io | INSTALL_K3S_EXEC='server --tls-san %s %s' INSTALL_K3S_VERSION='%s' sh -\n", ip, strings.TrimSpace(k3sExtraArgs), k3sVersion)
getConfigcommand := fmt.Sprintf(sudoPrefix + "cat /etc/rancher/k3s/k3s.yaml\n")
merge, _ := command.Flags().GetBool("merge")
k3sExtraArgs, _ := command.Flags().GetString("k3s-extra-args")
context, _ := command.Flags().GetString("context")

k3sVersion, _ := command.Flags().GetString("k3s-version")
if local {
operator := operator.ExecOperator{}

fmt.Printf("Executing: %s\n", installK3scommand)

res, err := operator.Execute(installK3scommand)
if err != nil {
return err
}

if len(res.StdErr) > 0 {
fmt.Printf("stderr: %q", res.StdErr)
}
if len(res.StdOut) > 0 {
fmt.Printf("stdout: %q", res.StdOut)
}

err = obtainKubeconfig(operator, getConfigcommand, ip.String(), context, localKubeconfig, merge)
if err != nil {
return err
}

return nil
}

port, _ := command.Flags().GetInt("ssh-port")

fmt.Println("Public IP: " + ip.String())

user, _ := command.Flags().GetString("user")
sshKey, _ := command.Flags().GetString("ssh-key")

sshKeyPath := expandPath(sshKey)
fmt.Printf("ssh -i %s %s@%s\n", sshKeyPath, user, ip.String())
Expand All @@ -89,7 +122,7 @@ func MakeInstall() *cobra.Command {
}

address := fmt.Sprintf("%s:%d", ip.String(), port)
operator, err := kssh.NewSSHOperator(address, config)
operator, err := operator.NewSSHOperator(address, config)

if err != nil {
return errors.Wrapf(err, "unable to connect to %s over ssh", address)
Expand All @@ -98,7 +131,6 @@ func MakeInstall() *cobra.Command {
defer operator.Close()

if !skipInstall {
installK3scommand := fmt.Sprintf("curl -sLS https://get.k3s.io | INSTALL_K3S_EXEC='server --tls-san %s %s' INSTALL_K3S_VERSION='%s' sh -\n", ip, strings.TrimSpace(k3sExtraArgs), k3sVersion)

fmt.Printf("ssh: %s\n", installK3scommand)
res, err := operator.Execute(installK3scommand)
Expand All @@ -110,32 +142,11 @@ func MakeInstall() *cobra.Command {
fmt.Printf("Result: %s %s\n", string(res.StdOut), string(res.StdErr))
}

getConfigcommand := fmt.Sprintf(sudoPrefix + "cat /etc/rancher/k3s/k3s.yaml\n")
fmt.Printf("ssh: %s\n", getConfigcommand)

res, err := operator.Execute(getConfigcommand)

err = obtainKubeconfig(operator, getConfigcommand, ip.String(), context, localKubeconfig, merge)
if err != nil {
return fmt.Errorf("Error received processing command: %s", err)
}

fmt.Printf("Result: %s %s\n", string(res.StdOut), string(res.StdErr))

absPath, _ := filepath.Abs(localKubeconfig)

kubeconfig := rewriteKubeconfig(string(res.StdOut), ip.String(), context)

if merge {
// Create a merged kubeconfig
kubeconfig, err = mergeConfigs(absPath, []byte(kubeconfig))
if err != nil {
return err
}
}

// Create a new kubeconfig
if writeErr := writeConfig(absPath, []byte(kubeconfig), false); writeErr != nil {
return writeErr
return err
}

return nil
Expand All @@ -156,11 +167,41 @@ func MakeInstall() *cobra.Command {
return command
}

func obtainKubeconfig(operator operator.CommandOperator, getConfigcommand, ip, context, localKubeconfig string, merge bool) error {

res, err := operator.Execute(getConfigcommand)

if err != nil {
return fmt.Errorf("Error received processing command: %s", err)
}

fmt.Printf("Result: %s %s\n", string(res.StdOut), string(res.StdErr))

absPath, _ := filepath.Abs(localKubeconfig)

kubeconfig := rewriteKubeconfig(string(res.StdOut), ip, context)

if merge {
// Create a merged kubeconfig
kubeconfig, err = mergeConfigs(absPath, []byte(kubeconfig))
if err != nil {
return err
}
}

// Create a new kubeconfig
if writeErr := writeConfig(absPath, []byte(kubeconfig), false); writeErr != nil {
return writeErr
}
return nil
}

// Generates config files give the path to file: string and the data: []byte
func writeConfig(path string, data []byte, suppressMessage bool) error {
absPath, _ := filepath.Abs(path)
if !suppressMessage {
fmt.Printf("Saving file to: %s\n", absPath)
fmt.Printf("\n# Test your cluster with:\nexport KUBECONFIG=%s\nkubectl get node -o wide\n", absPath)
}
writeErr := ioutil.WriteFile(absPath, []byte(data), 0600)
if writeErr != nil {
Expand Down
6 changes: 3 additions & 3 deletions pkg/cmd/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"strings"

config "github.com/alexellis/k3sup/pkg/config"
kssh "github.com/alexellis/k3sup/pkg/ssh"
operator "github.com/alexellis/k3sup/pkg/operator"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh"
Expand Down Expand Up @@ -86,7 +86,7 @@ func MakeJoin() *cobra.Command {
}

address := fmt.Sprintf("%s:%d", serverIP.String(), serverPort)
operator, err := kssh.NewSSHOperator(address, config)
operator, err := operator.NewSSHOperator(address, config)

if err != nil {
return errors.Wrapf(err, "unable to connect to %s over ssh", address)
Expand Down Expand Up @@ -153,7 +153,7 @@ func setupAgent(serverIP, ip net.IP, port int, user, sshKeyPath, joinToken, k3sE
}

address := fmt.Sprintf("%s:%d", ip.String(), port)
operator, err := kssh.NewSSHOperator(address, config)
operator, err := operator.NewSSHOperator(address, config)

if err != nil {
return errors.Wrapf(err, "unable to connect to %s over ssh", address)
Expand Down
31 changes: 31 additions & 0 deletions pkg/operator/operator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ssh

import (
goexecute "github.com/alexellis/go-execute/pkg/v1"
)

type CommandOperator interface {
Execute(command string) (CommandRes, error)
}

type ExecOperator struct {
}

func (ex ExecOperator) Execute(command string) (CommandRes, error) {

task := goexecute.ExecTask{
Command: command,
Shell: true,
}

res, err := task.Execute()
if err != nil {
return CommandRes{}, err
}

return CommandRes{
StdErr: []byte(res.Stderr),
StdOut: []byte(res.Stdout),
}, nil

}
20 changes: 10 additions & 10 deletions pkg/ssh/ssh.go → pkg/operator/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type SSHOperator struct {
conn *ssh.Client
}

func (s *SSHOperator) Close() error {
func (s SSHOperator) Close() error {

return s.conn.Close()
}
Expand All @@ -31,18 +31,18 @@ func NewSSHOperator(address string, config *ssh.ClientConfig) (*SSHOperator, err
return &operator, nil
}

func (s *SSHOperator) Execute(command string) (commandRes, error) {
func (s SSHOperator) Execute(command string) (CommandRes, error) {

sess, err := s.conn.NewSession()
if err != nil {
return commandRes{}, err
return CommandRes{}, err
}

defer sess.Close()

sessStdOut, err := sess.StdoutPipe()
if err != nil {
return commandRes{}, err
return CommandRes{}, err
}

output := bytes.Buffer{}
Expand All @@ -57,7 +57,7 @@ func (s *SSHOperator) Execute(command string) (commandRes, error) {
}()
sessStderr, err := sess.StderrPipe()
if err != nil {
return commandRes{}, err
return CommandRes{}, err
}

errorOutput := bytes.Buffer{}
Expand All @@ -73,21 +73,21 @@ func (s *SSHOperator) Execute(command string) (commandRes, error) {
wg.Wait()

if err != nil {
return commandRes{}, err
return CommandRes{}, err
}

return commandRes{
return CommandRes{
StdErr: errorOutput.Bytes(),
StdOut: output.Bytes(),
}, nil
}

type commandRes struct {
type CommandRes struct {
StdOut []byte
StdErr []byte
}

func executeCommand(cmd string) (commandRes, error) {
func executeCommand(cmd string) (CommandRes, error) {

return commandRes{}, nil
return CommandRes{}, nil
}

0 comments on commit ce08b1b

Please sign in to comment.