Skip to content

Commit 90acb7e

Browse files
committed
Adding capability to use SSH for remote prep-node.
This change introduces the use of SSHClient and simple library that uses golang ssh client to execute commands remotely. I have tested prep-node remotely, but nothing else. Will be doing more refactoring as more testing is performed.
1 parent 827cb62 commit 90acb7e

File tree

14 files changed

+338
-192
lines changed

14 files changed

+338
-192
lines changed

cmd/bootstrap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func bootstrapCmdRun(cmd *cobra.Command, args []string) {
5050
log.Fatalf("Unable to load context: %s", err.Error())
5151
}
5252

53-
c, err := clients.New(ctx.Fqdn)
53+
c, err := clients.New(ctx.Fqdn, clients.LocalExecutor{})
5454
if err != nil {
5555
log.Fatalf("Unable to load clients: %s", err.Error())
5656
}

cmd/context.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,6 @@ var contextCmdGet = &cobra.Command{
7878
}
7979

8080
func init() {
81-
createCmd.AddCommand(contextCmdCreate)
82-
getCmd.AddCommand(contextCmdGet)
81+
rootCmd.AddCommand(contextCmdCreate)
82+
rootCmd.AddCommand(contextCmdGet)
8383
}

cmd/prepNode.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/platform9/pf9ctl/pkg/pmk"
99
"github.com/platform9/pf9ctl/pkg/pmk/clients"
1010
"github.com/spf13/cobra"
11+
"io/ioutil"
1112
)
1213

1314
// prepNodeCmd represents the prepNode command
@@ -30,21 +31,27 @@ var (
3031
func init() {
3132
prepNodeCmd.Flags().StringVarP(&user, "user", "u", "", "ssh username for the nodes")
3233
prepNodeCmd.Flags().StringVarP(&password, "password", "p", "", "ssh password for the nodes")
33-
prepNodeCmd.Flags().StringVarP(&sshKey, "ssh-key", "s", "", "ssh key for connecting to the nodes")
34+
prepNodeCmd.Flags().StringVarP(&sshKey, "ssh-key", "s", "", "ssh key file for connecting to the nodes")
3435
prepNodeCmd.Flags().StringSliceVarP(&ips, "ips", "i", []string{}, "ips of host to be prepared")
3536
prepNodeCmd.Flags().BoolVarP(&floatingIP, "floating-ip", "f", false, "")
3637

3738
rootCmd.AddCommand(prepNodeCmd)
3839
}
3940

41+
4042
func prepNodeRun(cmd *cobra.Command, args []string) {
4143

4244
ctx, err := pmk.LoadContext(constants.Pf9DBLoc)
4345
if err != nil {
4446
log.Fatalf("Unable to load the context: %s\n", err.Error())
4547
}
46-
47-
c, err := clients.New(ctx.Fqdn)
48+
// TODO: there seems to be a bug, we will need multiple executors one per ip, so at this moment
49+
// it will only work with one remote host
50+
executor, err := getExecutor()
51+
if err != nil {
52+
log.Fatalf("Error connecting to host %s",err.Error())
53+
}
54+
c, err := clients.New(ctx.Fqdn, executor)
4855
if err != nil {
4956
log.Fatalf("Unable to load clients needed for the Cmd. Error: %s", err.Error())
5057
}
@@ -54,3 +61,38 @@ func prepNodeRun(cmd *cobra.Command, args []string) {
5461
log.Fatalf("Unable to prep node: %s\n", err.Error())
5562
}
5663
}
64+
65+
// checkAndValidateRemote check if any of the command line
66+
func checkAndValidateRemote() bool {
67+
foundRemote := false
68+
for _, ip := range ips {
69+
if ip != "localhost" && ip != "127.0.0.1" && ip != "::1" {
70+
// lets create a remote executor, but before that check if we got user and either of password or ssh-key
71+
if user =="" || (sshKey == "" && password == "") {
72+
log.Fatalf("please provider 'user' and one of 'password' or ''ssh-key'")
73+
}
74+
foundRemote = true
75+
return foundRemote
76+
}
77+
}
78+
log.Info("Using local exeuctor")
79+
return foundRemote
80+
}
81+
82+
83+
// getExecutor creates the right Executor
84+
func getExecutor() (clients.Executor, error) {
85+
if checkAndValidateRemote() {
86+
var pKey []byte
87+
var err error
88+
if sshKey != "" {
89+
pKey, err = ioutil.ReadFile(sshKey)
90+
if err != nil {
91+
log.Fatalf("Unale to read the sshKey %s, %s", sshKey, err.Error())
92+
}
93+
}
94+
return clients.NewRemoteExecutor(ips[0], 22, user, pKey, password)
95+
}
96+
log.Info("Using local exeuctor")
97+
return clients.LocalExecutor{}, nil
98+
}

cmd/root.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,15 @@ func Execute() {
4343
}
4444

4545
func initializeBaseDirs() (err error) {
46-
err = os.MkdirAll(constants.Pf9Dir, os.ModeDir)
47-
err = os.MkdirAll(constants.Pf9DBDir, os.ModeDir)
48-
err = os.MkdirAll(constants.Pf9LogDir, os.ModeDir)
49-
46+
err = os.MkdirAll(constants.Pf9Dir, 0700)
47+
if err != nil {
48+
return
49+
}
50+
err = os.MkdirAll(constants.Pf9DBDir, 0700)
51+
if err != nil {
52+
return
53+
}
54+
err = os.MkdirAll(constants.Pf9LogDir, 0700)
5055
return
5156
}
5257

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ require (
66
github.com/google/uuid v1.1.2
77
github.com/hashicorp/go-retryablehttp v0.6.7
88
github.com/mitchellh/go-homedir v1.1.0
9+
github.com/pkg/sftp v1.12.0
910
github.com/prometheus/common v0.4.0
1011
github.com/segmentio/backo-go v0.0.0-20200129164019-23eae7c10bd3 // indirect
1112
github.com/sethgrid/pester v1.1.0
1213
github.com/spf13/cobra v1.0.0
1314
github.com/spf13/viper v1.6.3
1415
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
1516
go.uber.org/zap v1.10.0
16-
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
17+
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
1718
gopkg.in/segmentio/analytics-go.v3 v3.1.0
1819
)

go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
6666
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
6767
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
6868
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
69+
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
70+
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
6971
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
7072
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
7173
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -88,6 +90,10 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn
8890
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
8991
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
9092
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
93+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
94+
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
95+
github.com/pkg/sftp v1.12.0 h1:/f3b24xrDhkhddlaobPe2JgBqfdt+gC/NYl0QY9IOuI=
96+
github.com/pkg/sftp v1.12.0/go.mod h1:fUqqXB5vEgVCZ131L+9say31RAri6aF6KDViawhxKK8=
9197
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
9298
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9399
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -133,6 +139,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
133139
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
134140
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
135141
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
142+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
136143
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
137144
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
138145
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -152,12 +159,14 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
152159
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
153160
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
154161
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
162+
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
155163
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
156164
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
157165
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
158166
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
159167
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
160168
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
169+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
161170
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
162171
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
163172
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -169,6 +178,8 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
169178
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
170179
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
171180
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
181+
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
182+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
172183
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
173184
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
174185
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -195,4 +206,5 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
195206
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
196207
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
197208
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
209+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
198210
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

main.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@
33
package main
44

55
import (
6-
"fmt"
7-
"os"
6+
//"fmt"
7+
//"os"
88

99
"github.com/platform9/pf9ctl/cmd"
1010
)
1111

1212
func main() {
13+
// the program may run a machine different from the one where the installation is happening
14+
// so removing this check. If this check needs to happen it should happen where the installation is
15+
// happening
1316
// Check if program is run using root privileges
14-
if os.Geteuid() != 0 {
15-
fmt.Println("This program requires root privileges. Please run the binary as a root user.")
16-
os.Exit(1)
17-
}
17+
//if os.Geteuid() != 0 {
18+
// fmt.Println("This program requires root privileges. Please run the binary as a root user.")
19+
// os.Exit(1)
20+
//}
1821
// Read the context variables.
1922
cmd.Execute()
2023
}

pkg/constants/constants.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import (
55
"path/filepath"
66
"time"
77
)
8-
8+
// Pkg structure is meant for other code to import it into their own
9+
// code base, this MUST not be here
910
var (
1011
homeDir, _ = os.UserHomeDir()
1112
//Pf9Dir is the base pf9dir

pkg/pmk/clients/clients.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ type Client struct {
1414

1515
// New creates the clients needed by the CLI
1616
// to interact with the external services.
17-
func New(fqdn string) (Client, error) {
17+
func New(fqdn string, executor Executor) (Client, error) {
1818
return Client{
1919
Resmgr: NewResmgr(fqdn),
2020
Keystone: NewKeystone(fqdn),
2121
Qbert: NewQbert(fqdn),
22-
Executor: ExecutorImpl{},
22+
Executor: executor,
2323
Segment: NewSegment(fqdn),
2424
}, nil
2525
}

pkg/pmk/clients/executor.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,61 @@
11
package clients
22

3-
import "os/exec"
3+
import (
4+
"os/exec"
5+
"github.com/platform9/pf9ctl/pkg/ssh"
6+
"github.com/platform9/pf9ctl/pkg/log"
7+
"fmt"
8+
)
49

10+
// Executor interace abstracts us from local or remote execution
511
type Executor interface {
612
Run(name string, args ...string) error
713
RunWithStdout(name string, args ...string) (string, error)
814
}
915

10-
type ExecutorImpl struct{}
16+
// LocalExecutor as the name implies executes commands locally
17+
type LocalExecutor struct{}
1118

12-
func (c ExecutorImpl) Run(name string, args ...string) error {
19+
// Run runs a command locally returning just success or failure
20+
func (c LocalExecutor) Run(name string, args ...string) error {
1321
cmd := exec.Command(name, args...)
1422
return cmd.Run()
1523
}
1624

17-
func (c ExecutorImpl) RunWithStdout(name string, args ...string) (string, error) {
25+
// RunWithStdout runs a command locally returning stdout and err
26+
func (c LocalExecutor) RunWithStdout(name string, args ...string) (string, error) {
1827
byt, err := exec.Command(name, args...).Output()
1928
return string(byt), err
2029
}
30+
31+
// RemoteExecutor as the name implies runs commands usign SSH on remote host
32+
type RemoteExecutor struct{
33+
Client ssh.Client
34+
}
35+
36+
// Run runs a command locally returning just success or failure
37+
func (r *RemoteExecutor) Run(name string, args ...string) error {
38+
_,err := r.RunWithStdout(name, args...)
39+
return err
40+
}
41+
42+
// RunWithStdout runs a command locally returning stdout and err
43+
func (r *RemoteExecutor) RunWithStdout(name string, args ...string) (string, error) {
44+
cmd := name
45+
for _, arg := range args {
46+
cmd = fmt.Sprintf("%s \"%s\"", cmd, arg)
47+
}
48+
stdout, stderr, err := r.Client.RunCommand(cmd)
49+
log.Debug("Running command ",cmd, "stdout:", string(stdout), "stderr:", string(stderr))
50+
return string(stdout), err
51+
}
52+
53+
// NewRemoteExecutor create an Executor interface to execute commands remotely
54+
func NewRemoteExecutor(host string, port int, username string, privateKey []byte, password string) (Executor, error) {
55+
client, err := ssh.NewClient(host, port, username, privateKey, password)
56+
if err != nil {
57+
return nil, err
58+
}
59+
re := &RemoteExecutor{Client: client}
60+
return re, nil
61+
}

0 commit comments

Comments
 (0)