feat(helm): add conditions and tags

This feature adds the ability to selectively control the loading of charts using entries in top chart's values.
When 'helm install --set tags.mytag=true', charts with that tag will be enabled unless disabled in parent by condition.
When 'helm install --set mychart.enabled=true', charts with that yaml path specified will be enabled.

Closes #1837
pull/1917/head
Justin Scott 8 years ago
parent 1fda4e8a60
commit 8ef733ca7d

@ -64,4 +64,10 @@ message Metadata {
// The API Version of this chart. // The API Version of this chart.
string apiVersion = 10; string apiVersion = 10;
// The condition to check to enable chart
string condition = 11;
// The tags to check to enable chart
string tags = 12;
} }

@ -17,10 +17,11 @@ package chartutil
import ( import (
"errors" "errors"
"log"
"strings"
"time" "time"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/chart"
) )
@ -55,8 +56,17 @@ type Dependency struct {
// Appending `index.yaml` to this string should result in a URL that can be // Appending `index.yaml` to this string should result in a URL that can be
// used to fetch the repository index. // used to fetch the repository index.
Repository string `json:"repository"` Repository string `json:"repository"`
// A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled )
Condition string `json:"condition"`
// Tags can be used to group charts for enabling/disabling together
Tags []string `json:"tags"`
// Enabled bool determines if chart should be loaded
Enabled bool `json:"enabled"`
} }
// ErrNoRequirementsFile to detect error condition
type ErrNoRequirementsFile error
// Requirements is a list of requirements for a chart. // Requirements is a list of requirements for a chart.
// //
// Requirements are charts upon which this chart depends. This expresses // Requirements are charts upon which this chart depends. This expresses
@ -106,3 +116,138 @@ func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) {
r := &RequirementsLock{} r := &RequirementsLock{}
return r, yaml.Unmarshal(data, r) return r, yaml.Unmarshal(data, r)
} }
// ProcessRequirementsConditions disables charts based on condition path value in values
func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
var cond string
var conds []string
if reqs != nil && len(reqs.Dependencies) > 0 {
for _, r := range reqs.Dependencies {
var hasTrue, hasFalse bool
cond = string(r.Condition)
// check for list
if len(cond) > 0 {
if strings.Contains(cond, ",") {
conds = strings.Split(strings.TrimSpace(cond), ",")
} else {
conds = []string{strings.TrimSpace(cond)}
}
for _, c := range conds {
if len(c) > 0 {
// retrieve value
vv, err := cvals.PathValue(c)
if err == nil {
if vv.(bool) {
hasTrue = true
}
if !vv.(bool) {
hasFalse = true
}
} else {
if _, ok := err.(ErrNoValue); !ok {
// this is a real error
log.Printf("Warning: PathValue returned error %v", err)
}
}
if vv != nil {
// got first value, break loop
break
}
}
}
if !hasTrue && hasFalse {
r.Enabled = false
} else {
if hasTrue {
r.Enabled = true
}
}
}
}
}
}
// ProcessRequirementsTags disables charts based on tags in values
func ProcessRequirementsTags(reqs *Requirements, cvals Values) {
vt, err := cvals.Table("tags")
if err != nil {
return
}
if reqs != nil && len(reqs.Dependencies) > 0 {
for _, r := range reqs.Dependencies {
if len(r.Tags) > 0 {
tags := r.Tags
var hasTrue, hasFalse bool
for _, k := range tags {
if b, ok := vt[k]; ok {
if b.(bool) {
hasTrue = true
}
if !b.(bool) {
hasFalse = true
}
}
}
if !hasTrue && hasFalse {
r.Enabled = false
} else {
if hasTrue || !hasTrue && !hasFalse {
r.Enabled = true
}
}
}
}
}
}
// ProcessRequirementsEnabled removes disabled charts from dependencies
func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
reqs, err := LoadRequirements(c)
if err != nil {
return ErrRequirementsNotFound
}
// set all to true
for _, lr := range reqs.Dependencies {
lr.Enabled = true
}
cvals, err := CoalesceValues(c, v)
if err != nil {
return err
}
// flag dependencies as enabled/disabled
ProcessRequirementsTags(reqs, cvals)
ProcessRequirementsConditions(reqs, cvals)
// make a map of charts to keep
rm := map[string]bool{}
for _, r := range reqs.Dependencies {
if !r.Enabled {
// remove disabled chart
rm[r.Name] = true
}
}
// don't keep disabled charts in new slice
cd := c.Dependencies[:0]
for _, n := range c.Dependencies {
if _, ok := rm[n.Metadata.Name]; !ok {
cd = append(cd, n)
}
}
// recursively call self to process sub dependencies
for _, t := range cd {
err := ProcessRequirementsEnabled(t, v)
// if its not just missing requirements file, return error
if nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil {
return nerr
}
}
c.Dependencies = cd
return nil
}

@ -15,7 +15,10 @@ limitations under the License.
package chartutil package chartutil
import ( import (
"sort"
"testing" "testing"
"k8s.io/helm/pkg/proto/hapi/chart"
) )
func TestLoadRequirements(t *testing.T) { func TestLoadRequirements(t *testing.T) {
@ -33,3 +36,173 @@ func TestLoadRequirementsLock(t *testing.T) {
} }
verifyRequirementsLock(t, c) verifyRequirementsLock(t, c)
} }
func TestRequirementsTagsNonValue(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// tags with no effect
v := &chart.Config{Raw: string("tags:\n nothinguseful: false\n\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart", "subchart1", "subcharta", "subchartb"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsTagsDisabledL1(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// tags disabling a group
v := &chart.Config{Raw: string("tags:\n front-end: false\n\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsTagsEnabledL1(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// tags disabling a group and enabling a different group
v := &chart.Config{Raw: string("tags:\n front-end: false\n\n back-end: true\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart", "subchart2", "subchartb", "subchartc"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsTagsDisabledL2(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// tags disabling only children
v := &chart.Config{Raw: string("tags:\n subcharta: false\n\n subchartb: false\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart", "subchart1"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsTagsDisabledL1Mixed(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// tags disabling all parents/children with additional tag re-enabling a parent
v := &chart.Config{Raw: string("tags:\n front-end: false\n\n subchart1: true\n\n back-end: false\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart", "subchart1"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsConditionsNonValue(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// tags with no effect
v := &chart.Config{Raw: string("subchart1:\n nothinguseful: false\n\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart", "subchart1", "subcharta", "subchartb"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsConditionsEnabledL1Both(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// conditions enabling the parent charts, effectively enabling children
v := &chart.Config{Raw: string("subchart1:\n enabled: true\nsubchart2:\n enabled: true\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb", "subchartc"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsConditionsDisabledL1Both(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// conditions disabling the parent charts, effectively disabling children
v := &chart.Config{Raw: string("subchart1:\n enabled: false\nsubchart2:\n enabled: false\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsConditionsSecond(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// conditions a child using the second condition path of child's condition
v := &chart.Config{Raw: string("subchart1:\n subcharta:\n enabled: false\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart", "subchart1", "subchartb"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsCombinedDisabledL2(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// tags enabling a parent/child group with condition disabling one child
v := &chart.Config{Raw: string("subchartc:\n enabled: false\ntags:\n back-end: true\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"}
verifyRequirementsEnabled(t, c, v, e)
}
func TestRequirementsCombinedDisabledL1(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
// tags will not enable a child if parent is explicitly disabled with condition
v := &chart.Config{Raw: string("subchart1:\n enabled: false\ntags:\n front-end: true\n")}
// expected charts including duplicates in alphanumeric order
e := []string{"parentchart"}
verifyRequirementsEnabled(t, c, v, e)
}
func verifyRequirementsEnabled(t *testing.T, c *chart.Chart, v *chart.Config, e []string) {
out := []*chart.Chart{}
err := ProcessRequirementsEnabled(c, v)
if err != nil {
t.Errorf("Error processing enabled requirements %v", err)
}
out = extractCharts(c, out)
// build list of chart names
p := []string{}
for _, r := range out {
p = append(p, r.Metadata.Name)
}
//sort alphanumeric and compare to expectations
sort.Strings(p)
if len(p) != len(e) {
t.Errorf("Error slice lengths do not match got %v, expected %v", len(p), len(e))
return
}
for i := range p {
if p[i] != e[i] {
t.Errorf("Error slice values do not match got %v, expected %v", p[i], e[i])
}
}
}
// extractCharts recursively searches chart dependencies returning all charts found
func extractCharts(c *chart.Chart, out []*chart.Chart) []*chart.Chart {
if len(c.Metadata.Name) > 0 {
out = append(out, c)
}
for _, d := range c.Dependencies {
out = extractCharts(d, out)
}
return out
}

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for Kubernetes
name: parentchart
version: 0.1.0

@ -0,0 +1,18 @@
## Subpop
This chart is for testing the processing of enabled/disabled charts
via conditions and tags.
Currently there are three levels:
````
parent
-1 tags: front-end, subchart1
--A tags: front-end, subchartA
--B tags: front-end, subchartB
-2 tags: back-end, subchart2
--B tags: back-end, subchartB
--C tags: back-end, subchartC
````
Tags and conditions are currently in requirements.yaml files.

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for Kubernetes
name: subchart1
version: 0.1.0

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for Kubernetes
name: subcharta
version: 0.1.0

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
protocol: TCP
name: {{ .Values.service.name }}
selector:
app: {{ .Chart.Name }}

@ -0,0 +1,21 @@
# Default values for subchart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
service:
name: nginx
type: ClusterIP
externalPort: 80
internalPort: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for Kubernetes
name: subchartb
version: 0.1.0

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
protocol: TCP
name: {{ .Values.service.name }}
selector:
app: {{ .Chart.Name }}

@ -0,0 +1,21 @@
# Default values for subchart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
service:
name: nginx
type: ClusterIP
externalPort: 80
internalPort: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi

@ -0,0 +1,15 @@
dependencies:
- name: subcharta
repository: http://localhost:10191
version: 0.1.0
condition: subcharta.enabled,subchart1.subcharta.enabled
tags:
- front-end
- subcharta
- name: subchartb
repository: http://localhost:10191
version: 0.1.0
condition: subchartb.enabled
tags:
- front-end
- subchartb

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
protocol: TCP
name: {{ .Values.service.name }}
selector:
app: {{ .Chart.Name }}

@ -0,0 +1,21 @@
# Default values for subchart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
service:
name: nginx
type: ClusterIP
externalPort: 80
internalPort: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for Kubernetes
name: subchart2
version: 0.1.0

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for Kubernetes
name: subchartb
version: 0.1.0

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: subchart2-{{ .Chart.Name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
protocol: TCP
name: subchart2-{{ .Values.service.name }}
selector:
app: {{ .Chart.Name }}

@ -0,0 +1,21 @@
# Default values for subchart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
service:
name: nginx
type: ClusterIP
externalPort: 80
internalPort: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi

@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for Kubernetes
name: subchartc
version: 0.1.0

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
protocol: TCP
name: {{ .Values.service.name }}
selector:
app: {{ .Chart.Name }}

@ -0,0 +1,21 @@
# Default values for subchart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
service:
name: nginx
type: ClusterIP
externalPort: 80
internalPort: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi

@ -0,0 +1,15 @@
dependencies:
- name: subchartb
repository: http://localhost:10191
version: 0.1.0
condition: subchartb.enabled,subchart2.subchartb.enabled
tags:
- back-end
- subchartb
- name: subchartc
repository: http://localhost:10191
version: 0.1.0
condition: subchartc.enabled
tags:
- back-end
- subchartc

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.externalPort }}
targetPort: {{ .Values.service.internalPort }}
protocol: TCP
name: {{ .Values.service.name }}
selector:
app: {{ .Chart.Name }}

@ -0,0 +1,21 @@
# Default values for subchart.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
service:
name: nginx
type: ClusterIP
externalPort: 80
internalPort: 80
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi

@ -0,0 +1,15 @@
dependencies:
- name: subchart1
repository: http://localhost:10191
version: 0.1.0
condition: subchart1.enabled
tags:
- front-end
- subchart1
- name: subchart2
repository: http://localhost:10191
version: 0.1.0
condition: subchart2.enabled
tags:
- back-end
- subchart2

@ -0,0 +1,7 @@
# parent/values.yaml
# switch-like
tags:
front-end: true
back-end: false

@ -31,6 +31,9 @@ import (
// ErrNoTable indicates that a chart does not have a matching table. // ErrNoTable indicates that a chart does not have a matching table.
type ErrNoTable error type ErrNoTable error
// ErrNoValue indicates that Values does not contain a key with a value
type ErrNoValue error
// GlobalKey is the name of the Values key that is used for storing global vars. // GlobalKey is the name of the Values key that is used for storing global vars.
const GlobalKey = "global" const GlobalKey = "global"
@ -376,3 +379,39 @@ func istable(v interface{}) bool {
_, ok := v.(map[string]interface{}) _, ok := v.(map[string]interface{})
return ok return ok
} }
// PathValue takes a yaml path with . notation and returns the value if exists
func (v Values) PathValue(ypath string) (interface{}, error) {
if len(ypath) == 0 {
return nil, error(fmt.Errorf("yaml path string cannot be zero length"))
}
yps := strings.Split(ypath, ".")
if len(ypath) == 1 {
// if exists must be root key not table
vals := v.AsMap()
k := yps[0]
if _, ok := vals[k]; ok && !istable(vals[k]) {
// key found
return vals[yps[0]], nil
}
// key not found
return nil, ErrNoValue(fmt.Errorf("%v is not a value", k))
}
table := yps[:len(yps)-1]
st := strings.Join(table, ".")
key := yps[len(yps)-1:]
sk := string(key[0])
t, err := v.Table(st)
if err != nil {
//no table
return nil, ErrNoValue(fmt.Errorf("%v is not a value", sk))
}
if k, ok := t[sk]; ok && !istable(k) {
// key found
return k, nil
}
// key not found
return nil, ErrNoValue(fmt.Errorf("key not found: %s", sk))
}

@ -407,3 +407,37 @@ func TestCoalesceTables(t *testing.T) {
t.Errorf("Expected boat string, got %v", dst["boat"]) t.Errorf("Expected boat string, got %v", dst["boat"])
} }
} }
func TestPathValue(t *testing.T) {
doc := `
title: "Moby Dick"
chapter:
one:
title: "Loomings"
two:
title: "The Carpet-Bag"
three:
title: "The Spouter Inn"
`
d, err := ReadValues([]byte(doc))
if err != nil {
t.Fatalf("Failed to parse the White Whale: %s", err)
}
if v, err := d.PathValue("chapter.one.title"); err != nil {
t.Errorf("Got error instead of title: %s\n%v", err, d)
} else if v != "Loomings" {
t.Errorf("No error but got wrong value for title: %s\n%v", err, d)
}
if _, err := d.PathValue("chapter.one.doesntexist"); err == nil {
t.Errorf("Non-existent key should return error: %s\n%v", err, d)
}
if _, err := d.PathValue("chapter.doesntexist.one"); err == nil {
t.Errorf("Non-existent key in middle of path should return error: %s\n%v", err, d)
}
if v, err := d.PathValue("title"); err == nil {
if v != "Moby Dick" {
t.Errorf("Failed to return values for root key title")
}
}
}

@ -92,6 +92,11 @@ func (h *Client) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ...
return nil, err return nil, err
} }
} }
err := chartutil.ProcessRequirementsEnabled(req.Chart, req.Values)
if err != nil {
return nil, err
}
return h.install(ctx, req) return h.install(ctx, req)
} }
@ -156,6 +161,11 @@ func (h *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts
return nil, err return nil, err
} }
} }
err := chartutil.ProcessRequirementsEnabled(req.Chart, req.Values)
if err != nil {
return nil, err
}
return h.update(ctx, req) return h.update(ctx, req)
} }

@ -71,6 +71,10 @@ type Metadata struct {
Icon string `protobuf:"bytes,9,opt,name=icon" json:"icon,omitempty"` Icon string `protobuf:"bytes,9,opt,name=icon" json:"icon,omitempty"`
// The API Version of this chart. // The API Version of this chart.
ApiVersion string `protobuf:"bytes,10,opt,name=apiVersion" json:"apiVersion,omitempty"` ApiVersion string `protobuf:"bytes,10,opt,name=apiVersion" json:"apiVersion,omitempty"`
// The condition to check to enable chart
Condition string `protobuf:"bytes,11,opt,name=condition" json:"condition,omitempty"`
// The tags to check to enable chart
Tags []string `protobuf:"bytes,12,opt,name=tags" json:"tags,omitempty"`
} }
func (m *Metadata) Reset() { *m = Metadata{} } func (m *Metadata) Reset() { *m = Metadata{} }
@ -94,24 +98,25 @@ func init() {
func init() { proto.RegisterFile("hapi/chart/metadata.proto", fileDescriptor2) } func init() { proto.RegisterFile("hapi/chart/metadata.proto", fileDescriptor2) }
var fileDescriptor2 = []byte{ var fileDescriptor2 = []byte{
// 290 bytes of a gzipped FileDescriptorProto // 311 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x91, 0x4d, 0x4b, 0xf4, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x91, 0xcd, 0x4b, 0xc3, 0x40,
0x14, 0x85, 0xdf, 0x4e, 0xbf, 0x6f, 0x37, 0xc3, 0xe5, 0x65, 0x88, 0x2e, 0xa4, 0x74, 0xd5, 0x55, 0x10, 0xc5, 0xed, 0x47, 0x92, 0x66, 0xe2, 0xa1, 0x0c, 0x52, 0x56, 0x11, 0x09, 0x3d, 0xf5, 0x94,
0x07, 0x14, 0xc4, 0xb5, 0x20, 0x2e, 0x74, 0x3a, 0x52, 0xfc, 0x00, 0x77, 0xb1, 0x0d, 0x36, 0x68, 0x82, 0x82, 0x78, 0x16, 0xc4, 0x83, 0xb6, 0x95, 0xe2, 0x07, 0x78, 0x5b, 0x93, 0xa5, 0x5d, 0x34,
0x93, 0x92, 0x44, 0xc5, 0xff, 0xe8, 0x8f, 0x92, 0xa6, 0x9d, 0x99, 0x2e, 0xdc, 0xdd, 0x73, 0x9e, 0xbb, 0x61, 0x77, 0x55, 0xfc, 0xe7, 0x45, 0x76, 0x92, 0x36, 0x39, 0x78, 0x9b, 0xf7, 0x5e, 0xe6,
0x9e, 0xdb, 0x9c, 0x04, 0x8e, 0x5a, 0xda, 0xf3, 0x75, 0xdd, 0x52, 0x65, 0xd6, 0x1d, 0x33, 0xb4, 0x0d, 0xbf, 0x2c, 0x1c, 0x6f, 0x79, 0x25, 0xe7, 0xf9, 0x96, 0x1b, 0x37, 0x2f, 0x85, 0xe3, 0x05,
0xa1, 0x86, 0x16, 0xbd, 0x92, 0x46, 0x22, 0x0c, 0xa8, 0xb0, 0x28, 0x3b, 0x07, 0xd8, 0x50, 0x2e, 0x77, 0x3c, 0xab, 0x8c, 0x76, 0x1a, 0xc1, 0x47, 0x19, 0x45, 0xd3, 0x4b, 0x80, 0x05, 0x97, 0xca,
0x0c, 0xe5, 0x82, 0x29, 0x44, 0xf0, 0x04, 0xed, 0x18, 0x71, 0x52, 0x27, 0x8f, 0x2b, 0x3b, 0xe3, 0x71, 0xa9, 0x84, 0x41, 0x84, 0xa1, 0xe2, 0xa5, 0x60, 0xbd, 0xb4, 0x37, 0x8b, 0xd7, 0x34, 0xe3,
0x7f, 0xf0, 0x59, 0x47, 0xf9, 0x3b, 0x59, 0x58, 0x73, 0x14, 0xd9, 0xcf, 0x02, 0xa2, 0xcd, 0xb4, 0x11, 0x04, 0xa2, 0xe4, 0xf2, 0x83, 0xf5, 0xc9, 0xac, 0xc5, 0xf4, 0xb7, 0x0f, 0xa3, 0x45, 0x53,
0xf6, 0xcf, 0x18, 0x82, 0xd7, 0xca, 0x8e, 0x4d, 0x29, 0x3b, 0x23, 0x81, 0x50, 0xcb, 0x0f, 0x55, 0xfb, 0xef, 0x1a, 0xc2, 0x70, 0xab, 0x4b, 0xd1, 0x6c, 0xd1, 0x8c, 0x0c, 0x22, 0xab, 0x3f, 0x4d,
0x33, 0x4d, 0xdc, 0xd4, 0xcd, 0xe3, 0x6a, 0x27, 0x07, 0xf2, 0xc9, 0x94, 0xe6, 0x52, 0x10, 0xcf, 0x2e, 0x2c, 0x1b, 0xa4, 0x83, 0x59, 0xbc, 0xde, 0x49, 0x9f, 0x7c, 0x09, 0x63, 0xa5, 0x56, 0x6c,
0x06, 0x76, 0x12, 0x53, 0x48, 0x1a, 0xa6, 0x6b, 0xc5, 0x7b, 0x33, 0x50, 0xdf, 0xd2, 0xb9, 0x85, 0x48, 0x0b, 0x3b, 0x89, 0x29, 0x24, 0x85, 0xb0, 0xb9, 0x91, 0x95, 0xf3, 0x69, 0x40, 0x69, 0xd7,
0xc7, 0x10, 0xbd, 0xb1, 0xef, 0x2f, 0xa9, 0x1a, 0x4d, 0x02, 0xbb, 0x76, 0xaf, 0xf1, 0x02, 0x92, 0xc2, 0x13, 0x18, 0xbd, 0x8b, 0x9f, 0x6f, 0x6d, 0x0a, 0xcb, 0x42, 0xaa, 0xdd, 0x6b, 0xbc, 0x82,
0x6e, 0x5f, 0x4f, 0x93, 0x30, 0x75, 0xf3, 0xe4, 0x74, 0x55, 0x1c, 0x2e, 0xa0, 0x38, 0xb4, 0xaf, 0xa4, 0xdc, 0xe3, 0x59, 0x16, 0xa5, 0x83, 0x59, 0x72, 0x3e, 0xc9, 0xda, 0x1f, 0x90, 0xb5, 0xf4,
0xe6, 0x9f, 0xe2, 0x0a, 0x02, 0x26, 0x5e, 0xb9, 0x60, 0x24, 0xb2, 0xbf, 0x9c, 0xd4, 0xd0, 0x8b, 0xeb, 0xee, 0xa7, 0x38, 0x81, 0x50, 0xa8, 0x8d, 0x54, 0x82, 0x8d, 0xe8, 0x64, 0xa3, 0x3c, 0x97,
0xd7, 0x52, 0x90, 0x78, 0xec, 0x35, 0xcc, 0x78, 0x02, 0x40, 0x7b, 0xfe, 0x38, 0x15, 0x00, 0x4b, 0xcc, 0xb5, 0x62, 0x71, 0xcd, 0xe5, 0x67, 0x3c, 0x03, 0xe0, 0x95, 0x7c, 0x6e, 0x00, 0x80, 0x92,
0x66, 0x4e, 0x96, 0x42, 0x70, 0x35, 0xa6, 0x13, 0x08, 0x1f, 0xca, 0x9b, 0x72, 0xfb, 0x54, 0x2e, 0x8e, 0x83, 0xa7, 0x10, 0xe7, 0x5a, 0x15, 0x92, 0x08, 0x12, 0x8a, 0x5b, 0xc3, 0x37, 0x3a, 0xbe,
0xff, 0x61, 0x0c, 0xfe, 0xf5, 0xf6, 0xfe, 0xee, 0x76, 0xe9, 0x5c, 0x86, 0xcf, 0xbe, 0x3d, 0xce, 0xb1, 0xec, 0xb0, 0x6e, 0xf4, 0xf3, 0x34, 0x85, 0xf0, 0xa6, 0xbe, 0x97, 0x40, 0xf4, 0xb4, 0xbc,
0x4b, 0x60, 0x9f, 0xe8, 0xec, 0x37, 0x00, 0x00, 0xff, 0xff, 0x65, 0x86, 0x8b, 0xda, 0xbf, 0x01, 0x5b, 0xae, 0x5e, 0x96, 0xe3, 0x03, 0x8c, 0x21, 0xb8, 0x5d, 0x3d, 0x3e, 0xdc, 0x8f, 0x7b, 0xd7,
0x00, 0x00, 0xd1, 0x6b, 0x40, 0x00, 0x6f, 0x21, 0x3d, 0xea, 0xc5, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5a,
0xa8, 0xcd, 0xe1, 0xf1, 0x01, 0x00, 0x00,
} }

@ -119,7 +119,7 @@ func TestResolve(t *testing.T) {
} }
func TestHashReq(t *testing.T) { func TestHashReq(t *testing.T) {
expect := "sha256:e70e41f8922e19558a8bf62f591a8b70c8e4622e3c03e5415f09aba881f13885" expect := "sha256:c8250374210bd909cef274be64f871bd4e376d4ecd34a1589b5abf90b68866ba"
req := &chartutil.Requirements{ req := &chartutil.Requirements{
Dependencies: []*chartutil.Dependency{ Dependencies: []*chartutil.Dependency{
{Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"}, {Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"},

Loading…
Cancel
Save