Add code to setup authentication on the remote cluster.

This still relies on a modified localkube build, which should be
fixed when we merge localkube, before this gets merged in.
pull/48/head
dlorenc 2016-04-29 11:28:42 -07:00 committed by Dan Lorenc
parent 512703be7b
commit 13e5937c02
5 changed files with 155 additions and 36 deletions

17
cli/tests/dir_utils.go Normal file
View File

@ -0,0 +1,17 @@
package tests
import (
"io/ioutil"
"log"
"k8s.io/minikube/cli/constants"
)
func MakeTempDir() string {
tempDir, err := ioutil.TempDir("", "minipath")
if err != nil {
log.Fatal(err)
}
constants.Minipath = tempDir
return tempDir
}

View File

@ -17,24 +17,13 @@ limitations under the License.
package cmd
import (
"io/ioutil"
"log"
"os"
"testing"
"github.com/spf13/cobra"
"k8s.io/minikube/pkg/minikube/constants"
"k8s.io/minikube/cli/tests"
)
func makeTempDir() string {
tempDir, err := ioutil.TempDir("", "minipath")
if err != nil {
log.Fatal(err)
}
constants.Minipath = tempDir
return tempDir
}
func runCommand(f func(*cobra.Command, []string)) {
cmd := cobra.Command{}
var args []string
@ -43,7 +32,7 @@ func runCommand(f func(*cobra.Command, []string)) {
func TestPreRunDirectories(t *testing.T) {
// Make sure we create the required directories.
tempDir := makeTempDir()
tempDir := tests.MakeTempDir()
defer os.RemoveAll(tempDir)
runCommand(RootCmd.PersistentPreRun)

View File

@ -57,11 +57,19 @@ func runStart(cmd *cobra.Command, args []string) {
if err != nil {
log.Println("Error connecting to cluster: ", err)
}
kubeHost = strings.Replace(kubeHost, "tcp://", "http://", -1)
kubeHost = strings.Replace(kubeHost, ":2376", ":8080", -1)
kubeHost = strings.Replace(kubeHost, "tcp://", "https://", -1)
kubeHost = strings.Replace(kubeHost, ":2376", ":443", -1)
log.Printf("Kubernetes is available at %s.\n", kubeHost)
log.Println("Run this command to use the cluster: ")
log.Printf("kubectl config set-cluster minikube --insecure-skip-tls-verify=true --server=%s\n", kubeHost)
log.Printf("kubectl config set-cluster minikube --server=%s --certificate-authority=$HOME/.minikube/ca.crt\n", kubeHost)
log.Println("kubectl config set-credentials minikube --client-certificate=$HOME/.minikube/kubecfg.crt --client-key=$HOME/.minikube/kubecfg.key")
log.Println("kubectl config set-context minikube --cluster=minikube --user=minikube")
log.Println("kubectl config use-context minikube")
if err := cluster.GetCreds(host); err != nil {
log.Println("Error configuring authentication: ", err)
os.Exit(1)
}
}
func init() {

View File

@ -19,7 +19,9 @@ package cluster
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"path/filepath"
"strings"
"time"
@ -30,6 +32,14 @@ import (
"k8s.io/minikube/pkg/minikube/constants"
)
const (
remotePath = "/srv/kubernetes/certs"
)
var (
certs = []string{"ca.crt", "kubecfg.key", "kubecfg.crt"}
)
// StartHost starts a host VM.
func StartHost(api libmachine.API) (*host.Host, error) {
if exists, err := api.Exists(constants.MachineName); err != nil {
@ -128,26 +138,45 @@ type sshAble interface {
RunSSHCommand(string) (string, error)
}
// StartCluster starts as k8s cluster on the specified Host.
// StartCluster starts a k8s cluster on the specified Host.
func StartCluster(h sshAble) error {
for _, cmd := range []string{
// Download and install weave, if it doesn't exist.
`if [ ! -e /usr/local/bin/weave ]; then
sudo curl -L git.io/weave -o /usr/local/bin/weave
sudo chmod a+x /usr/local/bin/weave;
fi`,
sudo curl -L git.io/weave -o /usr/local/bin/weave
sudo chmod a+x /usr/local/bin/weave;
fi`,
// Download and install localkube, if it doesn't exist yet.
`if [ ! -e /usr/local/bin/localkube ];
then
sudo curl -L https://github.com/redspread/localkube/releases/download/v1.2.1-v1/localkube-linux -o /usr/local/bin/localkube
sudo chmod a+x /usr/local/bin/localkube;
fi`,
`if [ ! -e /usr/local/bin/localkube ]; then
sudo curl -L https://storage.googleapis.com/tinykube/localkube -o /usr/local/bin/localkube
sudo chmod a+x /usr/local/bin/localkube;
fi`,
// Create certificates.
`sudo mkdir -p /srv/kubernetes/certs`,
`if [ ! -e easy-rsa.tar.gz ]; then
curl -L -O https://storage.googleapis.com/kubernetes-release/easy-rsa/easy-rsa.tar.gz
rm -rf easy-rsa-master
tar xzf easy-rsa.tar.gz
fi`,
`cert_ip=$(ip addr show ${interface} | grep 192.168 | sed -nEe 's/^[ \t]*inet[ \t]*([0-9.]+)\/.*$/\1/p')
ts=$(date +%s)
if ! grep $cert_ip /srv/kubernetes/certs/kubernetes-master.crt; then
cd easy-rsa-master/easyrsa3
./easyrsa init-pki
./easyrsa --batch "--req-cn=$cert_ip@$ts" build-ca nopass
./easyrsa --subject-alt-name="IP:$cert_ip" build-server-full kubernetes-master nopass
./easyrsa build-client-full kubecfg nopass
sudo cp -p pki/ca.crt /srv/kubernetes/certs/
sudo cp -p pki/issued/kubecfg.crt /srv/kubernetes/certs/
sudo cp -p pki/private/kubecfg.key /srv/kubernetes/certs/
sudo cp -p pki/issued/kubernetes-master.crt /srv/kubernetes/certs/
sudo cp -p pki/private/kubernetes-master.key /srv/kubernetes/certs/
fi`,
// Start weave.
"weave launch-router",
"weave launch-proxy --without-dns --rewrite-inspect",
"weave expose -h \"localkube.weave.local\"",
// Localkube assumes containerized kubelet, which looks at /rootfs.
"if [ ! -e /rootfs ]; then sudo ln -s / /rootfs; fi",
"sudo killall localkube || true",
// Run with nohup so it stays up. Redirect logs to useful places.
"PATH=/usr/local/sbin:$PATH nohup sudo /usr/local/bin/localkube start > /var/log/localkube.out 2> /var/log/localkube.err < /dev/null &"} {
output, err := h.RunSSHCommand(cmd)
@ -160,6 +189,24 @@ func StartCluster(h sshAble) error {
return nil
}
// GetCreds gets the generated credentials required to talk to the APIServer.
func GetCreds(h sshAble) error {
localPath := constants.Minipath
for _, cert := range certs {
remoteCertPath := filepath.Join(remotePath, cert)
localCertPath := filepath.Join(localPath, cert)
data, err := h.RunSSHCommand(fmt.Sprintf("cat %s", remoteCertPath))
if err != nil {
return err
}
if err := ioutil.WriteFile(localCertPath, []byte(data), 0644); err != nil {
return err
}
}
return nil
}
func createHost(api libmachine.API) (*host.Host, error) {
driver := virtualbox.NewDriver(constants.MachineName, constants.Minipath)
driver.Boot2DockerURL = "https://storage.googleapis.com/tinykube/boot2docker.iso"

View File

@ -18,6 +18,10 @@ package cluster
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
@ -57,12 +61,22 @@ func TestCreateHost(t *testing.T) {
}
}
// Mock Host used for testing. When commands are run, the output from CommandOutput
// is used, if present. Then the output from Error is used, if present. Finally,
// "", nil is returned.
type mockHost struct {
Commands []string
CommandOutput map[string]string
Error string
}
func (m mockHost) RunSSHCommand(cmd string) (string, error) {
m.Commands = append(m.Commands, cmd)
output, ok := m.CommandOutput[cmd]
if ok {
return output, nil
}
if m.Error != "" {
return "", fmt.Errorf(m.Error)
}
return "", nil
}
@ -74,14 +88,8 @@ func TestStartCluster(t *testing.T) {
}
}
type mockHostError struct{}
func (m mockHostError) RunSSHCommand(cmd string) (string, error) {
return "", fmt.Errorf("Error calling command: %s", cmd)
}
func TestStartClusterError(t *testing.T) {
h := mockHostError{}
h := mockHost{Error: "error"}
err := StartCluster(h)
if err == nil {
t.Fatal("Error not thrown starting cluster.")
@ -271,3 +279,53 @@ func TestGetHostStatus(t *testing.T) {
StopHost(api)
checkState(state.Stopped.String())
}
func TestGetCreds(t *testing.T) {
m := make(map[string]string)
for _, cert := range certs {
m[fmt.Sprintf("cat %s/%s", remotePath, cert)] = cert
}
h := mockHost{CommandOutput: m}
tempDir := tests.MakeTempDir()
defer os.RemoveAll(tempDir)
if err := GetCreds(h); err != nil {
t.Fatalf("Error starting cluster: %s", err)
}
for _, cert := range certs {
// Files should be created with contents matching the output.
certPath := filepath.Join(tempDir, cert)
contents, err := ioutil.ReadFile(certPath)
if err != nil {
t.Fatalf("Error %s reading file: %s", err, certPath)
}
if !reflect.DeepEqual(contents, []byte(cert)) {
t.Fatalf("Contents of file are: %s, should be %s", contents, cert)
}
}
}
func TestGetCredsError(t *testing.T) {
h := mockHost{
Error: "error getting creds",
}
tempDir := tests.MakeTempDir()
defer os.RemoveAll(tempDir)
if err := GetCreds(h); err == nil {
t.Fatalf("Error should have been thrown, but was not.")
}
// No files should have been created.
for _, cert := range certs {
certPath := filepath.Join(tempDir, cert)
_, err := os.Stat(certPath)
if !os.IsNotExist(err) {
t.Fatalf("File %s should not exist.", certPath)
}
}
}