Merge pull request #239 from aaron-prindle/control-kubernetes-version
Kubernetes/localkube versioning via a flag and testspull/433/head
commit
c493cbe50d
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
```
|
||||
|
|
|
@ -17,7 +17,9 @@ limitations under the License.
|
|||
package cluster
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -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"
|
||||
|
@ -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()}
|
||||
|
@ -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/",
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,10 @@ package cluster
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -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{
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
@ -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"
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package sshutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
}()
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package sshutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,12 @@ package util
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/minikube/pkg/minikube/constants"
|
||||
)
|
||||
|
||||
// Until endlessly loops the provided function until a message is received on the done channel.
|
||||
|
@ -76,3 +80,20 @@ func RetryAfter(attempts int, callback func() error, d time.Duration) (err error
|
|||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func GetLocalkubeDownloadURL(versionOrURL string, filename string) (string, error) {
|
||||
urlObj, err := url.Parse(versionOrURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if urlObj.IsAbs() {
|
||||
// scheme was specified in input, is a valid URI.
|
||||
// http.Get will catch unsupported schemes
|
||||
return versionOrURL, nil
|
||||
}
|
||||
if !strings.HasPrefix(versionOrURL, "v") {
|
||||
// no 'v' prefix in input, need to prepend it to version
|
||||
versionOrURL = "v" + versionOrURL
|
||||
}
|
||||
return fmt.Sprintf("%s%s/%s", constants.LocalkubeDownloadURLPrefix, versionOrURL, filename), nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue