Merge pull request #2229 from technosophos/feat/chart-tiller-version

feat(tiller): support version constraint on chart
pull/2260/head
Matt Butcher 7 years ago committed by GitHub
commit d97086cbdd

@ -73,7 +73,11 @@ message Metadata {
// The version of the application enclosed inside of this chart.
string appVersion = 13;
// Whether or not this chart is deprecated
bool deprecated = 14;
// TillerVersion is a SemVer constraints on what version of Tiller is required.
// See SemVer ranges here: https://github.com/Masterminds/semver#basic-comparisons
string tillerVersion = 15;
}

@ -38,4 +38,22 @@ There are a few small conventions followed for using the words Helm, helm, Tille
- `tiller` is the name of the binary run on the backend
- The term 'chart' does not need to be capitalized, as it is not a proper noun.
When in doubt, use _Helm_ (with an uppercase 'H').
When in doubt, use _Helm_ (with an uppercase 'H').
## Restricting Tiller by Version
A `Chart.yaml` file can specify a `tillerVersion` SemVer constraint:
```yaml
name: mychart
version: 0.2.0
tillerVersion: ">=2.4.0"
```
This constraint should be set when templates use a new feature that was not
supported in older versions of Helm. While this parameter will accept sophisticated
SemVer rules, the best practice is to default to the form `>=2.4.0`, where `2.4.0`
is the version that introduced the new feature used in the chart.
This feature was introduced in Helm 2.4.0, so any version of Tiller older than
2.4.0 will simply ignore this field.

@ -55,6 +55,7 @@ engine: gotpl # The name of the template engine (optional, defaults to gotpl)
icon: A URL to an SVG or PNG image to be used as an icon (optional).
appVersion: The version of the app that this contains (optional). This needn't be SemVer.
deprecated: Whether or not this chart is deprecated (optional, boolean)
tillerVersion: The version of Tiller that this chart requires. This should be expressed as a SemVer range: ">2.0.0" (optional)
```
If you are familiar with the `Chart.yaml` file format for Helm Classic, you will

@ -79,6 +79,9 @@ type Metadata struct {
AppVersion string `protobuf:"bytes,13,opt,name=appVersion" json:"appVersion,omitempty"`
// Whether or not this chart is deprecated
Deprecated bool `protobuf:"varint,14,opt,name=deprecated" json:"deprecated,omitempty"`
// TillerVersion is a SemVer constraints on what version of Tiller is required.
// See SemVer ranges here: https://github.com/Masterminds/semver#basic-comparisons
TillerVersion string `protobuf:"bytes,15,opt,name=tillerVersion" json:"tillerVersion,omitempty"`
}
func (m *Metadata) Reset() { *m = Metadata{} }
@ -102,27 +105,28 @@ func init() {
func init() { proto.RegisterFile("hapi/chart/metadata.proto", fileDescriptor2) }
var fileDescriptor2 = []byte{
// 339 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x92, 0x4f, 0x4b, 0xeb, 0x40,
0x14, 0xc5, 0x5f, 0x5e, 0x9a, 0x7f, 0x37, 0xef, 0x3d, 0xca, 0xf0, 0x28, 0xa3, 0x88, 0x84, 0xae,
0xb2, 0x4a, 0x41, 0x41, 0x5c, 0x0b, 0xe2, 0x42, 0xdb, 0x4a, 0xf0, 0x0f, 0xb8, 0x1b, 0x93, 0x4b,
0x3b, 0x68, 0x66, 0xc2, 0xcc, 0xa8, 0xf8, 0xe5, 0xfc, 0x6c, 0x32, 0x93, 0xa4, 0xcd, 0xc2, 0xdd,
0x3d, 0xe7, 0x97, 0x7b, 0x92, 0x73, 0x09, 0x1c, 0x6c, 0x59, 0xcb, 0x17, 0xd5, 0x96, 0x29, 0xb3,
0x68, 0xd0, 0xb0, 0x9a, 0x19, 0x56, 0xb4, 0x4a, 0x1a, 0x49, 0xc0, 0xa2, 0xc2, 0xa1, 0xf9, 0x19,
0xc0, 0x92, 0x71, 0x61, 0x18, 0x17, 0xa8, 0x08, 0x81, 0x89, 0x60, 0x0d, 0x52, 0x2f, 0xf3, 0xf2,
0xa4, 0x74, 0x33, 0xf9, 0x0f, 0x01, 0x36, 0x8c, 0xbf, 0xd2, 0xdf, 0xce, 0xec, 0xc4, 0xfc, 0xcb,
0x87, 0x78, 0xd9, 0xc7, 0xfe, 0xb8, 0x46, 0x60, 0xb2, 0x95, 0x0d, 0xf6, 0x5b, 0x6e, 0x26, 0x14,
0x22, 0x2d, 0xdf, 0x54, 0x85, 0x9a, 0xfa, 0x99, 0x9f, 0x27, 0xe5, 0x20, 0x2d, 0x79, 0x47, 0xa5,
0xb9, 0x14, 0x74, 0xe2, 0x16, 0x06, 0x49, 0x32, 0x48, 0x6b, 0xd4, 0x95, 0xe2, 0xad, 0xb1, 0x34,
0x70, 0x74, 0x6c, 0x91, 0x43, 0x88, 0x5f, 0xf0, 0xf3, 0x43, 0xaa, 0x5a, 0xd3, 0xd0, 0xc5, 0xee,
0x34, 0x39, 0x87, 0xb4, 0xd9, 0xd5, 0xd3, 0x34, 0xca, 0xfc, 0x3c, 0x3d, 0x99, 0x15, 0xfb, 0x03,
0x14, 0xfb, 0xf6, 0xe5, 0xf8, 0x51, 0x32, 0x83, 0x10, 0xc5, 0x86, 0x0b, 0xa4, 0xb1, 0x7b, 0x65,
0xaf, 0x6c, 0x2f, 0x5e, 0x49, 0x41, 0x93, 0xae, 0x97, 0x9d, 0xc9, 0x31, 0x00, 0x6b, 0xf9, 0x43,
0x5f, 0x00, 0x1c, 0x19, 0x39, 0xe4, 0x08, 0x92, 0x4a, 0x8a, 0x9a, 0xbb, 0x06, 0xa9, 0xc3, 0x7b,
0xc3, 0x26, 0x1a, 0xb6, 0xd1, 0xf4, 0x4f, 0x97, 0x68, 0xe7, 0x2e, 0xb1, 0x1d, 0x12, 0xff, 0x0e,
0x89, 0x83, 0x63, 0x79, 0x8d, 0xad, 0xc2, 0x8a, 0x19, 0xac, 0xe9, 0xbf, 0xcc, 0xcb, 0xe3, 0x72,
0xe4, 0xcc, 0x33, 0x08, 0x2f, 0xbb, 0xef, 0x4d, 0x21, 0xba, 0x5f, 0x5d, 0xaf, 0xd6, 0x8f, 0xab,
0xe9, 0x2f, 0x92, 0x40, 0x70, 0xb5, 0xbe, 0xbb, 0xbd, 0x99, 0x7a, 0x17, 0xd1, 0x53, 0xe0, 0x0e,
0xf0, 0x1c, 0xba, 0x9f, 0xe2, 0xf4, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x08, 0xf3, 0xcc, 0x66, 0x31,
0x02, 0x00, 0x00,
// 354 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x92, 0x5f, 0x4b, 0xe3, 0x40,
0x14, 0xc5, 0x37, 0x9b, 0xe6, 0xdf, 0xcd, 0x76, 0xb7, 0x0c, 0x4b, 0x99, 0x5d, 0x44, 0x42, 0xf1,
0x21, 0x4f, 0x29, 0x28, 0x88, 0xcf, 0x82, 0xf8, 0xa0, 0x6d, 0x25, 0xf8, 0x07, 0x7c, 0x1b, 0x93,
0x4b, 0x3b, 0xd8, 0xcc, 0x84, 0xc9, 0xa8, 0xf8, 0x7d, 0xfd, 0x20, 0x32, 0x93, 0xa4, 0x8d, 0xe0,
0xdb, 0x3d, 0xe7, 0xe4, 0xfe, 0xc2, 0xb9, 0x0c, 0xfc, 0xdb, 0xb0, 0x9a, 0xcf, 0x8b, 0x0d, 0x53,
0x7a, 0x5e, 0xa1, 0x66, 0x25, 0xd3, 0x2c, 0xab, 0x95, 0xd4, 0x92, 0x80, 0x89, 0x32, 0x1b, 0xcd,
0x4e, 0x01, 0x16, 0x8c, 0x0b, 0xcd, 0xb8, 0x40, 0x45, 0x08, 0x8c, 0x04, 0xab, 0x90, 0x3a, 0x89,
0x93, 0x46, 0xb9, 0x9d, 0xc9, 0x5f, 0xf0, 0xb0, 0x62, 0x7c, 0x4b, 0x7f, 0x5a, 0xb3, 0x15, 0xb3,
0x0f, 0x17, 0xc2, 0x45, 0x87, 0xfd, 0x76, 0x8d, 0xc0, 0x68, 0x23, 0x2b, 0xec, 0xb6, 0xec, 0x4c,
0x28, 0x04, 0x8d, 0x7c, 0x51, 0x05, 0x36, 0xd4, 0x4d, 0xdc, 0x34, 0xca, 0x7b, 0x69, 0x92, 0x57,
0x54, 0x0d, 0x97, 0x82, 0x8e, 0xec, 0x42, 0x2f, 0x49, 0x02, 0x71, 0x89, 0x4d, 0xa1, 0x78, 0xad,
0x4d, 0xea, 0xd9, 0x74, 0x68, 0x91, 0xff, 0x10, 0x3e, 0xe3, 0xfb, 0x9b, 0x54, 0x65, 0x43, 0x7d,
0x8b, 0xdd, 0x69, 0x72, 0x06, 0x71, 0xb5, 0xab, 0xd7, 0xd0, 0x20, 0x71, 0xd3, 0xf8, 0x78, 0x9a,
0xed, 0x0f, 0x90, 0xed, 0xdb, 0xe7, 0xc3, 0x4f, 0xc9, 0x14, 0x7c, 0x14, 0x6b, 0x2e, 0x90, 0x86,
0xf6, 0x97, 0x9d, 0x32, 0xbd, 0x78, 0x21, 0x05, 0x8d, 0xda, 0x5e, 0x66, 0x26, 0x87, 0x00, 0xac,
0xe6, 0xf7, 0x5d, 0x01, 0xb0, 0xc9, 0xc0, 0x21, 0x07, 0x10, 0x15, 0x52, 0x94, 0xdc, 0x36, 0x88,
0x6d, 0xbc, 0x37, 0x0c, 0x51, 0xb3, 0x75, 0x43, 0x7f, 0xb5, 0x44, 0x33, 0xb7, 0xc4, 0xba, 0x27,
0x8e, 0x7b, 0x62, 0xef, 0x98, 0xbc, 0xc4, 0x5a, 0x61, 0xc1, 0x34, 0x96, 0xf4, 0x77, 0xe2, 0xa4,
0x61, 0x3e, 0x70, 0xc8, 0x11, 0x8c, 0x35, 0xdf, 0x6e, 0x51, 0xf5, 0x88, 0x3f, 0x16, 0xf1, 0xd5,
0x9c, 0x25, 0xe0, 0x5f, 0xb4, 0xad, 0x62, 0x08, 0xee, 0x96, 0x57, 0xcb, 0xd5, 0xc3, 0x72, 0xf2,
0x83, 0x44, 0xe0, 0x5d, 0xae, 0x6e, 0x6f, 0xae, 0x27, 0xce, 0x79, 0xf0, 0xe8, 0xd9, 0x33, 0x3d,
0xf9, 0xf6, 0xe9, 0x9c, 0x7c, 0x06, 0x00, 0x00, 0xff, 0xff, 0xea, 0xb5, 0x4c, 0xbe, 0x57, 0x02,
0x00, 0x00,
}

@ -776,6 +776,13 @@ func getVersionSet(client discovery.ServerGroupsInterface) (chartutil.VersionSet
}
func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values, vs chartutil.VersionSet) ([]*release.Hook, *bytes.Buffer, string, error) {
// Guard to make sure Tiller is at the right version to handle this chart.
sver := version.GetVersion()
if ch.Metadata.TillerVersion != "" &&
!version.IsCompatibleRange(ch.Metadata.TillerVersion, sver) {
return nil, nil, "", fmt.Errorf("Chart incompatible with Tiller %s", sver)
}
renderer := s.engine(ch)
files, err := renderer.Render(ch, values)
if err != nil {

@ -325,7 +325,7 @@ func TestInstallRelease(t *testing.T) {
}
}
func TestInstallReleaseWithNotes(t *testing.T) {
func TestInstallRelease_WithNotes(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
@ -394,7 +394,7 @@ func TestInstallReleaseWithNotes(t *testing.T) {
}
}
func TestInstallReleaseWithNotesRendered(t *testing.T) {
func TestInstallRelease_WithNotesRendered(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
@ -464,7 +464,54 @@ func TestInstallReleaseWithNotesRendered(t *testing.T) {
}
}
func TestInstallReleaseWithChartAndDependencyNotes(t *testing.T) {
func TestInstallRelease_TillerVersion(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
// TODO: Refactor this into a mock.
req := &services.InstallReleaseRequest{
Namespace: "spaced",
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello", TillerVersion: ">=2.2.0"},
Templates: []*chart.Template{
{Name: "templates/hello", Data: []byte("hello: world")},
{Name: "templates/hooks", Data: []byte(manifestWithHook)},
},
},
}
_, err := rs.InstallRelease(c, req)
if err != nil {
t.Fatalf("Expected valid range. Got %q", err)
}
}
func TestInstallRelease_WrongTillerVersion(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
// TODO: Refactor this into a mock.
req := &services.InstallReleaseRequest{
Namespace: "spaced",
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello", TillerVersion: "<2.0.0"},
Templates: []*chart.Template{
{Name: "templates/hello", Data: []byte("hello: world")},
{Name: "templates/hooks", Data: []byte(manifestWithHook)},
},
},
}
_, err := rs.InstallRelease(c, req)
if err == nil {
t.Fatalf("Expected to fail because of wrong version")
}
expect := "Chart incompatible with Tiller"
if !strings.Contains(err.Error(), expect) {
t.Errorf("Expected %q to contain %q", err.Error(), expect)
}
}
func TestInstallRelease_WithChartAndDependencyNotes(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
@ -515,7 +562,7 @@ func TestInstallReleaseWithChartAndDependencyNotes(t *testing.T) {
}
}
func TestInstallReleaseDryRun(t *testing.T) {
func TestInstallRelease_DryRun(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
@ -568,7 +615,7 @@ func TestInstallReleaseDryRun(t *testing.T) {
}
}
func TestInstallReleaseNoHooks(t *testing.T) {
func TestInstallRelease_NoHooks(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
rs.env.Releases.Create(releaseStub())
@ -587,7 +634,7 @@ func TestInstallReleaseNoHooks(t *testing.T) {
}
}
func TestInstallReleaseFailedHooks(t *testing.T) {
func TestInstallRelease_FailedHooks(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
rs.env.Releases.Create(releaseStub())
@ -606,7 +653,7 @@ func TestInstallReleaseFailedHooks(t *testing.T) {
}
}
func TestInstallReleaseReuseName(t *testing.T) {
func TestInstallRelease_ReuseName(t *testing.T) {
c := helm.NewContext()
rs := rsFixture()
rel := releaseStub()

@ -38,6 +38,17 @@ func IsCompatible(client, server string) bool {
constraint = cv.String()
}
return IsCompatibleRange(constraint, server)
}
// IsCompatibleRange compares a version to a constraint.
// It returns true if the version matches the constraint, and false in all other cases.
func IsCompatibleRange(constraint, ver string) bool {
sv, err := semver.NewVersion(ver)
if err != nil {
return false
}
c, err := semver.NewConstraint(constraint)
if err != nil {
return false

@ -41,3 +41,26 @@ func TestIsCompatible(t *testing.T) {
}
}
}
func TestIsCompatibleRange(t *testing.T) {
tests := []struct {
constraint string
ver string
expected bool
}{
{"v2.0.0-alpha.4", "v2.0.0-alpha.4", true},
{"v2.0.0-alpha.3", "v2.0.0-alpha.4", false},
{"v2.0.0", "v2.0.0-alpha.4", false},
{"v2.0.0-alpha.4", "v2.0.0", false},
{"~v2.0.0", "v2.0.1", true},
{"v2", "v2.0.0", true},
{">2.0.0", "v2.1.1", true},
{"v2.1.*", "v2.1.1", true},
}
for _, tt := range tests {
if IsCompatibleRange(tt.constraint, tt.ver) != tt.expected {
t.Errorf("expected constraint %s to be %v for %s", tt.constraint, tt.expected, tt.ver)
}
}
}

Loading…
Cancel
Save