-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
k3s.go
201 lines (163 loc) · 4.34 KB
/
k3s.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package k3s
import (
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"runtime"
"strings"
"time"
homedir "github.com/mitchellh/go-homedir"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
)
const (
k3sUpdateAPI = "https://update.k3s.io"
k3sChannelsEndpoint = "/v1-release/channels"
apiTimeout = 10 * time.Second
)
const maxRetries = 6 // number of times to try ssh'ing into the VM
const retryDelay = 5 * time.Second // time between retries
// use cases:
// build latest version of k3s
// build specific version of k3s
// check if there is a newer version of a stream (ie 1.18)
// Manager will handle installation of k3s
type Manager struct {
Releases ReleaseInfo
}
// NewManager will create a new k3s manager
func NewManager() *Manager {
m := new(Manager)
err := m.Releases.LoadChannels()
if err != nil {
log.Error("error: could not load list of k3s channels")
}
err = m.Releases.LoadReleases()
if err != nil {
log.Error("could not load list of k3s releases")
}
return m
}
// CheckRequirements will make sure required components are installed
func (m *Manager) CheckRequirements() (bool, error) {
if runtime.GOOS == "windows" {
return false, errors.New("tool does not support windows yet")
}
return true, nil
}
// Install k3s on given VM
func (m *Manager) Install(ipAddress string, k3sVersion string, appName string) bool {
user := "root"
sshPort := 22
// build install command
installK3scommand := fmt.Sprintf("curl -sLS https://get.k3s.io | INSTALL_K3S_VERSION=%s sh -\n", k3sVersion)
// log.Debug(installK3scommand)
// get the private sshkey
// TODO: should support ssh agent & passphrases
keyFile := "~/.ssh/id_rsa"
sshPrivateKeyFile, _ := homedir.Expand(keyFile)
passphrase := ""
signer, err := getSSHKey(sshPrivateKeyFile, passphrase)
if err != nil {
log.Error(err)
return false
}
// setup ssh details
config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
address := fmt.Sprintf("%s:%d", ipAddress, sshPort)
// ssh into the server (& retry if can't)
numRetries := 0
conn := &ssh.Client{}
for numRetries < maxRetries {
// try and ssh into vm
conn, err = ssh.Dial("tcp", address, config)
if err == nil {
// able to ssh into vm
break
}
log.Debug(err)
// wait a few seconds
time.Sleep(retryDelay)
numRetries += 1
}
if err != nil {
return false
}
// install k3s on the VM
output, err := runCommand(conn, installK3scommand)
if err != nil {
log.Error(err)
log.Debug(output)
return false
}
log.Info("k3s installed on VM")
// get kubectl config
getK3sConfigCommand := "cat /etc/rancher/k3s/k3s.yaml\n"
output, err = runCommand(conn, getK3sConfigCommand)
if err != nil {
log.Error(err)
log.Debug(string(output))
return false
}
// log.Debug(string(output))
// need to update kubeconfig so works outside the VM
// IP address needs to be set to external IP
// also rename context to something other than 'default'
context := appName
configUpdater := strings.NewReplacer(
"127.0.0.1", ipAddress,
"localhost", ipAddress,
"default", context,
)
kubectlConfig := configUpdater.Replace(output)
// save output to kubectrl config file
// TODO: option to merge?
absPath, _ := filepath.Abs("kubeconfig")
err = ioutil.WriteFile(absPath, []byte(kubectlConfig), 0600)
if err != nil {
log.Error(err)
return false
}
log.Info("kubernetes is initializing")
log.Info("you can access using `kubectl --kubeconfig ./kubeconfig get pods`")
return true
}
func runCommand(conn *ssh.Client, command string) (outputStr string, err error) {
sess, err := conn.NewSession()
if err != nil {
log.Error(err)
return "", err
}
defer sess.Close()
output, err := sess.CombinedOutput(command)
outputStr = string(output)
if err != nil {
log.Error(err)
log.Debug(outputStr)
return outputStr, err
}
// log.Debug(outputStr)
return outputStr, nil
}
// getSshKey
func getSSHKey(keyFilename string, passphrase string) (signer ssh.Signer, err error) {
// load the private key
privateKey, err := ioutil.ReadFile(keyFilename)
if err != nil {
return nil, err
}
// decode the key
if passphrase != "" {
signer, err = ssh.ParsePrivateKeyWithPassphrase(privateKey, []byte(passphrase))
} else {
signer, err = ssh.ParsePrivateKey(privateKey)
}
return signer, err
}