Commit fa06dd17 authored by Florian Zysset's avatar Florian Zysset Committed by Matthew Fisher

helm init --upgrade will downgrade (#2805)

* Don't downgrade tiller with helm init --upgrade unless --force-upgrade is specified
Fix tests after merging master

* Reformatting with gofmt
parent 70192cda
......@@ -76,6 +76,7 @@ type initCmd struct {
upgrade bool
namespace string
dryRun bool
forceUpgrade bool
skipRefresh bool
out io.Writer
home helmpath.Home
......@@ -106,6 +107,7 @@ func newInitCmd(out io.Writer) *cobra.Command {
f.StringVarP(&i.image, "tiller-image", "i", "", "override Tiller image")
f.BoolVar(&i.canary, "canary-image", false, "use the canary Tiller image")
f.BoolVar(&i.upgrade, "upgrade", false, "upgrade if Tiller is already installed")
f.BoolVar(&i.forceUpgrade, "force-upgrade", false, "force upgrade of Tiller to the current helm version")
f.BoolVarP(&i.clientOnly, "client-only", "c", false, "if set does not install Tiller")
f.BoolVar(&i.dryRun, "dry-run", false, "do not install local or remote")
f.BoolVar(&i.skipRefresh, "skip-refresh", false, "do not refresh (download) the local repository cache")
......@@ -164,6 +166,7 @@ func (i *initCmd) run() error {
i.opts.Namespace = i.namespace
i.opts.UseCanary = i.canary
i.opts.ImageSpec = i.image
i.opts.ForceUpgrade = i.forceUpgrade
i.opts.ServiceAccount = i.serviceAccount
i.opts.MaxHistory = i.maxHistory
......
......@@ -17,10 +17,12 @@ limitations under the License.
package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"errors"
"fmt"
"io/ioutil"
"strings"
"github.com/Masterminds/semver"
"github.com/ghodss/yaml"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
......@@ -30,6 +32,7 @@ import (
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
extensionsclient "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
"k8s.io/helm/pkg/version"
"k8s.io/helm/pkg/chartutil"
)
......@@ -60,6 +63,10 @@ func Upgrade(client kubernetes.Interface, opts *Options) error {
if err != nil {
return err
}
existingImage := obj.Spec.Template.Spec.Containers[0].Image
if !isNewerVersion(existingImage) && !opts.ForceUpgrade {
return errors.New("current Tiller version is newer, use --force-upgrade to downgrade")
}
obj.Spec.Template.Spec.Containers[0].Image = opts.selectImage()
obj.Spec.Template.Spec.Containers[0].ImagePullPolicy = opts.pullPolicy()
obj.Spec.Template.Spec.ServiceAccountName = opts.ServiceAccount
......@@ -75,6 +82,17 @@ func Upgrade(client kubernetes.Interface, opts *Options) error {
return err
}
// isNewerVersion returns whether the current version is newer than the give image's version
func isNewerVersion(image string) bool {
split := strings.Split(image, ":")
if len(split) < 2 {
// If we don't know the version, we consider the current version newer
return true
}
imageVersion := split[1]
return semver.MustParse(version.Version).GreaterThan(semver.MustParse(imageVersion))
}
// createDeployment creates the Tiller Deployment resource.
func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error {
obj, err := deployment(opts)
......
......@@ -337,7 +337,7 @@ func TestUpgrade(t *testing.T) {
serviceAccount := "newServiceAccount"
existingDeployment, _ := deployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace",
ImageSpec: "imageToReplace:v1.0.0",
ServiceAccount: "serviceAccountToReplace",
UseCanary: false,
})
......@@ -416,6 +416,66 @@ func TestUpgrade_serviceNotFound(t *testing.T) {
}
}
func TestUgrade_newerVersion(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
serviceAccount := "newServiceAccount"
existingDeployment, _ := deployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace:v100.5.0",
ServiceAccount: "serviceAccountToReplace",
UseCanary: false,
})
existingService := service(v1.NamespaceDefault)
fc := &fake.Clientset{}
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
return true, existingDeployment, nil
})
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
}
sa := obj.Spec.Template.Spec.ServiceAccountName
if sa != serviceAccount {
t.Errorf("expected serviceAccountName = '%s', got '%s'", serviceAccount, sa)
}
return true, obj, nil
})
fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) {
return true, existingService, nil
})
opts := &Options{
Namespace: v1.NamespaceDefault,
ImageSpec: image,
ServiceAccount: serviceAccount,
ForceUpgrade: false,
}
if err := Upgrade(fc, opts); err == nil {
t.Errorf("Expected error because the deployed version is newer")
}
if actions := fc.Actions(); len(actions) != 1 {
t.Errorf("unexpected actions: %v, expected 1 action got %d", actions, len(actions))
}
opts = &Options{
Namespace: v1.NamespaceDefault,
ImageSpec: image,
ServiceAccount: serviceAccount,
ForceUpgrade: true,
}
if err := Upgrade(fc, opts); err != nil {
t.Errorf("unexpected error: %#+v", err)
}
if actions := fc.Actions(); len(actions) != 4 {
t.Errorf("unexpected actions: %v, expected 4 action got %d", actions, len(actions))
}
}
func tlsTestFile(t *testing.T, path string) string {
const tlsTestDir = "../../../testdata"
path = filepath.Join(tlsTestDir, path)
......
......@@ -47,6 +47,9 @@ type Options struct {
// ServiceAccount is the Kubernetes service account to add to Tiller.
ServiceAccount string
// Force allows to force upgrading tiller if deployed version is greater than current version
ForceUpgrade bool
// ImageSpec indentifies the image Tiller will use when deployed.
//
// Valid if and only if UseCanary is false.
......
......@@ -36,6 +36,7 @@ helm init
--canary-image use the canary Tiller image
-c, --client-only if set does not install Tiller
--dry-run do not install local or remote
--force-upgrade force upgrade of Tiller to the current helm version
--history-max int limit the maximum number of revisions saved per release. Use 0 for no limit.
--local-repo-url string URL for local repository (default "http://127.0.0.1:8879/charts")
--net-host install Tiller with net=host
......@@ -68,4 +69,4 @@ helm init
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 7-Nov-2017
###### Auto generated by spf13/cobra on 9-Jan-2018
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment