Skip to content

Commit

Permalink
Merge pull request kubernetes#239 from aaron-prindle/control-kubernet…
Browse files Browse the repository at this point in the history
…es-version

Kubernetes/localkube versioning via a flag and tests
  • Loading branch information
aaron-prindle authored Aug 1, 2016
2 parents 38a918f + 0b1cfce commit c493cbe
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 30 deletions.
30 changes: 17 additions & 13 deletions cmd/minikube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@ import (
)

var (
minikubeISO string
memory int
cpus int
disk = newUnitValue(20 * units.GB)
vmDriver string
dockerEnv []string
insecureRegistry []string
minikubeISO string
memory int
cpus int
disk = newUnitValue(20 * units.GB)
vmDriver string
dockerEnv []string
insecureRegistry []string
kubernetesVersion string
)

// startCmd represents the start command
Expand All @@ -67,6 +68,9 @@ func runStart(cmd *cobra.Command, args []string) {
DockerEnv: dockerEnv,
InsecureRegistry: insecureRegistry,
}
kubernetesConfig := cluster.KubernetesConfig{
KubernetesVersion: kubernetesVersion,
}

var host *host.Host
start := func() (err error) {
Expand All @@ -79,7 +83,7 @@ func runStart(cmd *cobra.Command, args []string) {
os.Exit(1)
}

if err := cluster.UpdateCluster(host.Driver); err != nil {
if err := cluster.UpdateCluster(host, host.Driver, kubernetesConfig); err != nil {
glog.Errorln("Error updating cluster: ", err)
os.Exit(1)
}
Expand Down Expand Up @@ -157,14 +161,14 @@ func setupKubeconfig(name, server, certAuth, cliCert, cliKey string) error {
}

func init() {
startCmd.Flags().StringVarP(&minikubeISO, "iso-url", "", constants.DefaultIsoUrl, "Location of the minikube iso")
startCmd.Flags().StringVarP(&vmDriver, "vm-driver", "", constants.DefaultVMDriver, fmt.Sprintf("VM driver is one of: %v", constants.SupportedVMDrivers))
startCmd.Flags().IntVarP(&memory, "memory", "", constants.DefaultMemory, "Amount of RAM allocated to the minikube VM")
startCmd.Flags().IntVarP(&cpus, "cpus", "", constants.DefaultCPUS, "Number of CPUs allocated to the minikube VM")
startCmd.Flags().StringVar(&minikubeISO, "iso-url", constants.DefaultIsoUrl, "Location of the minikube iso")
startCmd.Flags().StringVar(&vmDriver, "vm-driver", constants.DefaultVMDriver, fmt.Sprintf("VM driver is one of: %v", constants.SupportedVMDrivers))
startCmd.Flags().IntVar(&memory, "memory", constants.DefaultMemory, "Amount of RAM allocated to the minikube VM")
startCmd.Flags().IntVar(&cpus, "cpus", constants.DefaultCPUS, "Number of CPUs allocated to the minikube VM")
diskFlag := startCmd.Flags().VarPF(disk, "disk-size", "", "Disk size allocated to the minikube VM (format: <number>[<unit>], where unit = b, k, m or g)")
diskFlag.DefValue = constants.DefaultDiskSize

startCmd.Flags().StringSliceVar(&dockerEnv, "docker-env", nil, "Environment variables to pass to the Docker daemon. (format: key=value)")
startCmd.Flags().StringSliceVar(&insecureRegistry, "insecure-registry", nil, "Insecure Docker registries to pass to the Docker daemon")
startCmd.Flags().StringVar(&kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "The kubernetes version that the minikube VM will (ex: v1.2.3) \n OR a URI which contains a localkube binary (ex: https://storage.googleapis.com/minikube/k8sReleases/v1.3.0/localkube-linux-amd64)")
RootCmd.AddCommand(startCmd)
}
2 changes: 2 additions & 0 deletions docs/minikube_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ minikube start
--docker-env=[]: Environment variables to pass to the Docker daemon. (format: key=value)
--insecure-registry=[]: Insecure Docker registries to pass to the Docker daemon
--iso-url="https://storage.googleapis.com/minikube/minikube-0.5.iso": Location of the minikube iso
--kubernetes-version="v1.3.3": The kubernetes version that the minikube VM will (ex: v1.2.3)
OR a URI which contains a localkube binary (ex: https://storage.googleapis.com/minikube/k8sReleases/v1.3.0/localkube-linux-amd64)
--memory=1024: Amount of RAM allocated to the minikube VM
--vm-driver="virtualbox": VM driver is one of: [virtualbox vmwarefusion kvm xhyve]
```
Expand Down
70 changes: 61 additions & 9 deletions pkg/minikube/cluster/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ limitations under the License.
package cluster

import (
"bytes"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
Expand All @@ -36,6 +38,7 @@ import (
"github.com/docker/machine/libmachine/host"
"github.com/docker/machine/libmachine/state"
"github.com/golang/glog"
"golang.org/x/crypto/ssh"
kubeApi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
Expand Down Expand Up @@ -177,6 +180,11 @@ type MachineConfig struct {
InsecureRegistry []string
}

// KubernetesConfig contains the parameters used to configure the VM Kubernetes.
type KubernetesConfig struct {
KubernetesVersion string
}

// StartCluster starts a k8s cluster on the specified Host.
func StartCluster(h sshAble) error {
commands := []string{stopCommand, GetStartCommand()}
Expand All @@ -201,12 +209,6 @@ type fileToCopy struct {
}

var assets = []fileToCopy{
{
AssetName: "out/localkube",
TargetDir: "/usr/local/bin",
TargetName: "localkube",
Permissions: "0777",
},
{
AssetName: "deploy/iso/addon-manager.yaml",
TargetDir: "/etc/kubernetes/manifests/",
Expand All @@ -227,11 +229,56 @@ var assets = []fileToCopy{
},
}

func UpdateCluster(d drivers.Driver) error {
func updateLocalkubeFromURL(config KubernetesConfig, client *ssh.Client) error {
resp := &http.Response{}
err := errors.New("")
downloader := func() (err error) {
url, err := util.GetLocalkubeDownloadURL(config.KubernetesVersion,
constants.LocalkubeLinuxFilename)
if err != nil {
return err
}
resp, err = http.Get(url)
return err
}

if err = util.Retry(5, downloader); err != nil {
return err
}
if err = sshutil.Transfer(resp.Body, int(resp.ContentLength), "/usr/local/bin",
"localkube", "0777", client); err != nil {
return err
}
return nil
}

func updateLocalkubeFromAsset(client *ssh.Client) error {
contents, err := Asset("out/localkube")
if err != nil {
glog.Infof("Error loading asset out/localkube: %s", err)
return err
}
if err := sshutil.Transfer(bytes.NewReader(contents), len(contents), "/usr/local/bin",
"localkube", "0777", client); err != nil {
return err
}
return nil
}

func UpdateCluster(h sshAble, d drivers.Driver, config KubernetesConfig) error {
client, err := sshutil.NewSSHClient(d)
if err != nil {
return err
}
if localkubeURLWasSpecified(config) {
if err = updateLocalkubeFromURL(config, client); err != nil {
return err
}
} else {
if err = updateLocalkubeFromAsset(client); err != nil {
return err
}
}

for _, a := range assets {
contents, err := Asset(a.AssetName)
Expand All @@ -240,13 +287,18 @@ func UpdateCluster(d drivers.Driver) error {
return err
}

if err := sshutil.Transfer(contents, a.TargetDir, a.TargetName, a.Permissions, client); err != nil {
if err := sshutil.Transfer(bytes.NewReader(contents), len(contents), a.TargetDir, a.TargetName, a.Permissions, client); err != nil {
return err
}
}
return nil
}

func localkubeURLWasSpecified(config KubernetesConfig) bool {
// see if flag is different than default -> it was passed by user
return config.KubernetesVersion != constants.DefaultKubernetesVersion
}

// SetupCerts gets the generated credentials required to talk to the APIServer.
func SetupCerts(d drivers.Driver) error {
localPath := constants.Minipath
Expand Down Expand Up @@ -280,7 +332,7 @@ func SetupCerts(d drivers.Driver) error {
if strings.HasSuffix(cert, ".key") {
perms = "0600"
}
if err := sshutil.Transfer(data, util.DefaultCertPath, cert, perms, client); err != nil {
if err := sshutil.Transfer(bytes.NewReader(data), len(data), util.DefaultCertPath, cert, perms, client); err != nil {
return err
}
}
Expand Down
60 changes: 58 additions & 2 deletions pkg/minikube/cluster/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ package cluster
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
Expand Down Expand Up @@ -502,13 +505,14 @@ func TestGetServiceURLWithoutNodePort(t *testing.T) {
}
}

func TestUpdate(t *testing.T) {
func TestUpdateDefault(t *testing.T) {
s, _ := tests.NewSSHServer()
port, err := s.Start()
if err != nil {
t.Fatalf("Error starting ssh server: %s", err)
}

h := tests.NewMockHost()
d := &tests.MockDriver{
Port: port,
BaseDriver: drivers.BaseDriver{
Expand All @@ -517,15 +521,67 @@ func TestUpdate(t *testing.T) {
},
}

if err := UpdateCluster(d); err != nil {
kubernetesConfig := KubernetesConfig{
KubernetesVersion: constants.DefaultKubernetesVersion,
}

if err := UpdateCluster(h, d, kubernetesConfig); err != nil {
t.Fatalf("Error updating cluster: %s", err)
}
transferred := s.Transfers.Bytes()

//test that kube-add on assets are transferred properly
for _, a := range assets {
contents, _ := Asset(a.AssetName)
if !bytes.Contains(transferred, contents) {
t.Fatalf("File not copied. Expected transfers to contain: %s. It was: %s", contents, transferred)
}
}

//test that localkube is transferred properly
contents, _ := Asset("out/localkube")
if !bytes.Contains(transferred, contents) {
t.Fatalf("File not copied. Expected transfers to contain: %s. It was: %s", contents, transferred)
}
}

var testLocalkubeBin = "hello"

type K8sVersionHandlerCorrect struct{}

func (h *K8sVersionHandlerCorrect) ServeHTTP(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, testLocalkubeBin)
}

func TestUpdateKubernetesVersion(t *testing.T) {
s, _ := tests.NewSSHServer()
port, err := s.Start()
if err != nil {
t.Fatalf("Error starting ssh server: %s", err)
}

h := tests.NewMockHost()
d := &tests.MockDriver{
Port: port,
BaseDriver: drivers.BaseDriver{
IPAddress: "127.0.0.1",
SSHKeyPath: "",
},
}
handler := &K8sVersionHandlerCorrect{}
server := httptest.NewServer(handler)

kubernetesConfig := KubernetesConfig{
KubernetesVersion: server.URL,
}
if err := UpdateCluster(h, d, kubernetesConfig); err != nil {
t.Fatalf("Error updating cluster: %s", err)
}
transferred := s.Transfers.Bytes()

//test that localkube is transferred properly
contents := []byte(testLocalkubeBin)
if !bytes.Contains(transferred, contents) {
t.Fatalf("File not copied. Expected transfers to contain: %s. It was: %s", contents, transferred)
}
}
1 change: 0 additions & 1 deletion pkg/minikube/cluster/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ var startCommandFmtStr = `
# Run with nohup so it stays up. Redirect logs to useful places.
sudo sh -c 'PATH=/usr/local/sbin:$PATH nohup /usr/local/bin/localkube %s --generate-certs=false --logtostderr=true > %s 2> %s < /dev/null &'
`

var logsCommand = fmt.Sprintf("tail -n +1 %s %s", constants.RemoteLocalKubeErrPath, constants.RemoteLocalKubeOutPath)

func GetStartCommand() string {
Expand Down
8 changes: 8 additions & 0 deletions pkg/minikube/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/util/homedir"
"k8s.io/kubernetes/pkg/version"
)

// MachineName is the name to use for the VM.
Expand Down Expand Up @@ -59,7 +60,14 @@ const (
DefaultVMDriver = "virtualbox"
)

var DefaultKubernetesVersion = version.Get().GitVersion

const (
RemoteLocalKubeErrPath = "/var/lib/localkube/localkube.err"
RemoteLocalKubeOutPath = "/var/lib/localkube/localkube.out"
)

var ConfigFilePath = MakeMiniPath("config")

var LocalkubeDownloadURLPrefix = "https://storage.googleapis.com/minikube/k8sReleases/"
var LocalkubeLinuxFilename = "localkube-linux-amd64"
6 changes: 2 additions & 4 deletions pkg/minikube/sshutil/sshutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package sshutil

import (
"bytes"
"fmt"
"io"
"path/filepath"
Expand Down Expand Up @@ -60,7 +59,7 @@ func NewSSHClient(d drivers.Driver) (*ssh.Client, error) {
}

// Transfer uses an SSH session to copy a file to the remote machine.
func Transfer(data []byte, remotedir, filename string, perm string, c *ssh.Client) error {
func Transfer(reader io.Reader, readerLen int, remotedir, filename string, perm string, c *ssh.Client) error {
// Delete the old file first. This makes sure permissions get reset.
deleteCmd := fmt.Sprintf("sudo rm -f %s", filepath.Join(remotedir, filename))
mkdirCmd := fmt.Sprintf("sudo mkdir -p %s", remotedir)
Expand All @@ -86,9 +85,8 @@ func Transfer(data []byte, remotedir, filename string, perm string, c *ssh.Clien
go func() {
defer wg.Done()
defer w.Close()
header := fmt.Sprintf("C%s %d %s\n", perm, len(data), filename)
header := fmt.Sprintf("C%s %d %s\n", perm, readerLen, filename)
fmt.Fprint(w, header)
reader := bytes.NewReader(data)
io.Copy(w, reader)
fmt.Fprint(w, "\x00")
}()
Expand Down
4 changes: 3 additions & 1 deletion pkg/minikube/sshutil/sshutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package sshutil

import (
"bytes"
"testing"

"github.com/docker/machine/libmachine/drivers"
Expand Down Expand Up @@ -109,7 +110,8 @@ func TestTransfer(t *testing.T) {
}

dest := "bar"
if err := Transfer([]byte("testcontents"), "/tmp", dest, "0777", c); err != nil {
contents := []byte("testcontents")
if err := Transfer(bytes.NewReader(contents), len(contents), "/tmp", dest, "0777", c); err != nil {
t.Fatalf("Unexpected error: %s", err)
}
}
Loading

0 comments on commit c493cbe

Please sign in to comment.