pull/4261/head
tariq1890 7 years ago
commit 45b69bdbd7

@ -53,7 +53,7 @@ An issue that we are not sure we will be doing will not be added to any mileston
A milestone (and hence release) is considered done when all outstanding issues/PRs have been closed or moved to another milestone.
## Semver
## Semantic Versioning
Helm maintains a strong commitment to backward compatibility. All of our changes to protocols and formats are backward compatible from Helm 2.0 until Helm 3.0. No features, flags, or commands are removed or substantially modified (other than bug fixes).

@ -2,7 +2,7 @@ DOCKER_REGISTRY ?= gcr.io
IMAGE_PREFIX ?= kubernetes-helm
SHORT_NAME ?= tiller
SHORT_NAME_RUDDER ?= rudder
TARGETS ?= darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64
TARGETS ?= darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64
DIST_DIRS = find * -type d -exec
APP = helm

@ -0,0 +1,20 @@
# Defined below are the security contacts for this repo.
#
# They are the contact point for the Product Security Team to reach out
# to for triaging and handling of incoming issues.
#
# The below names agree to abide by the
# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy)
# and will be removed and replaced if they violate that agreement.
#
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
# INSTRUCTIONS AT https://github.com/kubernetes/helm/blob/master/CONTRIBUTING.md#reporting-a-security-issue
adamreese
bacongobbler
mattfarina
michelleN
prydonius
SlickNik
technosophos
thomastaylor312

@ -312,10 +312,13 @@ func (i *initCmd) run() error {
"(Use --client-only to suppress this message, or --upgrade to upgrade Tiller to the current version.)")
}
} else {
fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been installed into your Kubernetes Cluster.\n\n"+
"Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.\n"+
fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been installed into your Kubernetes Cluster.")
if !tlsVerify {
fmt.Fprintln(i.out, "\nPlease note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.\n"+
"To prevent this, run `helm init` with the --tiller-tls-verify flag.\n"+
"For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation")
}
}
if err := i.ping(); err != nil {
return err
}

@ -258,6 +258,12 @@ func (i *installCmd) run() error {
if err := man.Update(); err != nil {
return prettyError(err)
}
// Update all dependencies which are present in /charts.
chartRequested, err = chartutil.Load(i.chartPath)
if err != nil {
return prettyError(err)
}
} else {
return prettyError(err)
}

@ -50,7 +50,7 @@ type Options struct {
// 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.
// ImageSpec identifies the image Tiller will use when deployed.
//
// Valid if and only if UseCanary is false.
ImageSpec string

@ -17,10 +17,12 @@ limitations under the License.
package main
import (
"encoding/json"
"fmt"
"io"
"strings"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
@ -75,6 +77,22 @@ type listCmd struct {
pending bool
client helm.Interface
colWidth uint
output string
}
type listResult struct {
Next string
Releases []listRelease
}
type listRelease struct {
Name string
Revision int32
Updated string
Status string
Chart string
AppVersion string
Namespace string
}
func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
@ -114,6 +132,7 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&list.pending, "pending", false, "show pending releases")
f.StringVar(&list.namespace, "namespace", "", "show releases within a specific namespace")
f.UintVar(&list.colWidth, "col-width", 60, "specifies the max column width of output")
f.StringVar(&list.output, "output", "", "output the specified format (json or yaml)")
// TODO: Do we want this as a feature of 'helm list'?
//f.BoolVar(&list.superseded, "history", true, "show historical releases")
@ -148,23 +167,17 @@ func (l *listCmd) run() error {
return prettyError(err)
}
if len(res.GetReleases()) == 0 {
return nil
}
rels := filterList(res.Releases)
if res.Next != "" && !l.short {
fmt.Fprintf(l.out, "\tnext: %s\n", res.Next)
}
result := getListResult(rels, res.Next)
rels := filterList(res.Releases)
output, err := formatResult(l.output, l.short, result, l.colWidth)
if l.short {
for _, r := range rels {
fmt.Fprintln(l.out, r.Name)
}
return nil
if err != nil {
return prettyError(err)
}
fmt.Fprintln(l.out, formatList(rels, l.colWidth))
fmt.Fprintln(l.out, output)
return nil
}
@ -233,23 +246,98 @@ func (l *listCmd) statusCodes() []release.Status_Code {
return status
}
func formatList(rels []*release.Release, colWidth uint) string {
table := uitable.New()
table.MaxColWidth = colWidth
table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "NAMESPACE")
func getListResult(rels []*release.Release, next string) listResult {
listReleases := []listRelease{}
for _, r := range rels {
md := r.GetChart().GetMetadata()
c := fmt.Sprintf("%s-%s", md.GetName(), md.GetVersion())
t := "-"
if tspb := r.GetInfo().GetLastDeployed(); tspb != nil {
t = timeconv.String(tspb)
}
s := r.GetInfo().GetStatus().GetCode().String()
v := r.GetVersion()
a := md.GetAppVersion()
n := r.GetNamespace()
table.AddRow(r.GetName(), v, t, s, c, a, n)
lr := listRelease{
Name: r.GetName(),
Revision: r.GetVersion(),
Updated: t,
Status: r.GetInfo().GetStatus().GetCode().String(),
Chart: fmt.Sprintf("%s-%s", md.GetName(), md.GetVersion()),
AppVersion: md.GetAppVersion(),
Namespace: r.GetNamespace(),
}
listReleases = append(listReleases, lr)
}
return listResult{
Releases: listReleases,
Next: next,
}
}
func shortenListResult(result listResult) []string {
names := []string{}
for _, r := range result.Releases {
names = append(names, r.Name)
}
return names
}
func formatResult(format string, short bool, result listResult, colWidth uint) (string, error) {
var output string
var err error
var shortResult []string
var finalResult interface{}
if short {
shortResult = shortenListResult(result)
finalResult = shortResult
} else {
finalResult = result
}
switch format {
case "":
if short {
output = formatTextShort(shortResult)
} else {
output = formatText(result, colWidth)
}
case "json":
o, e := json.Marshal(finalResult)
if e != nil {
err = fmt.Errorf("Failed to Marshal JSON output: %s", e)
} else {
output = string(o)
}
case "yaml":
o, e := yaml.Marshal(finalResult)
if e != nil {
err = fmt.Errorf("Failed to Marshal YAML output: %s", e)
} else {
output = string(o)
}
default:
err = fmt.Errorf("Unknown output format \"%s\"", format)
}
return table.String()
return output, err
}
func formatText(result listResult, colWidth uint) string {
nextOutput := ""
if result.Next != "" {
nextOutput = fmt.Sprintf("\tnext: %s\n", result.Next)
}
table := uitable.New()
table.MaxColWidth = colWidth
table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "NAMESPACE")
for _, lr := range result.Releases {
table.AddRow(lr.Name, lr.Revision, lr.Updated, lr.Status, lr.Chart, lr.AppVersion, lr.Namespace)
}
return fmt.Sprintf("%s%s", nextOutput, table.String())
}
func formatTextShort(shortResult []string) string {
return strings.Join(shortResult, "\n")
}

@ -18,16 +18,18 @@ package main
import (
"io"
"regexp"
"testing"
"github.com/spf13/cobra"
"io/ioutil"
"os"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release"
"os"
)
func TestListCmd(t *testing.T) {
@ -46,6 +48,11 @@ func TestListCmd(t *testing.T) {
ch, _ := chartutil.Load(chartPath)
tests := []releaseCase{
{
name: "empty",
rels: []*release.Release{},
expected: "",
},
{
name: "with a release",
rels: []*release.Release{
@ -67,6 +74,77 @@ func TestListCmd(t *testing.T) {
},
expected: "NAME \tREVISION\tUPDATED \tSTATUS \tCHART \tAPP VERSION\tNAMESPACE\natlas\t1 \t(.*)\tDEPLOYED\tfoo-0.1.0-beta.1\t2.X.A \tdefault \n",
},
{
name: "with json output",
flags: []string{"--max", "1", "--output", "json"},
rels: []*release.Release{
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"}),
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide"}),
},
expected: regexp.QuoteMeta(`{"Next":"atlas-guide","Releases":[{"Name":"thomas-guide","Revision":1,"Updated":"`) + `([^"]*)` + regexp.QuoteMeta(`","Status":"DEPLOYED","Chart":"foo-0.1.0-beta.1","AppVersion":"","Namespace":"default"}]}
`),
},
{
name: "with yaml output",
flags: []string{"--max", "1", "--output", "yaml"},
rels: []*release.Release{
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"}),
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas-guide"}),
},
expected: regexp.QuoteMeta(`Next: atlas-guide
Releases:
- AppVersion: ""
Chart: foo-0.1.0-beta.1
Name: thomas-guide
Namespace: default
Revision: 1
Status: DEPLOYED
Updated: `) + `(.*)` + `
`,
},
{
name: "with short json output",
flags: []string{"-q", "--output", "json"},
rels: []*release.Release{
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas"}),
},
expected: regexp.QuoteMeta(`["atlas"]
`),
},
{
name: "with short yaml output",
flags: []string{"-q", "--output", "yaml"},
rels: []*release.Release{
helm.ReleaseMock(&helm.MockReleaseOptions{Name: "atlas"}),
},
expected: regexp.QuoteMeta(`- atlas
`),
},
{
name: "with json output without next",
flags: []string{"--output", "json"},
rels: []*release.Release{},
expected: regexp.QuoteMeta(`{"Next":"","Releases":[]}
`),
},
{
name: "with yaml output without next",
flags: []string{"--output", "yaml"},
rels: []*release.Release{},
expected: regexp.QuoteMeta(`Next: ""
Releases: []
`),
},
{
name: "with unknown output format",
flags: []string{"--output", "_unknown_"},
rels: []*release.Release{},
err: true,
expected: regexp.QuoteMeta(``),
},
{
name: "list, one deployed, one failed",
flags: []string{"-q"},

@ -68,8 +68,8 @@ func TestTemplateCmd(t *testing.T) {
},
{
name: "check_execute_non_existent",
desc: "verify --execute fails on a template that doesnt exist",
args: []string{subchart1ChartPath, "-x", "templates/thisdoesntexist.yaml"},
desc: "verify --execute fails on a template that doesn't exist",
args: []string{subchart1ChartPath, "-x", "templates/thisdoesn'texist.yaml"},
expectError: "could not find template",
},
{

@ -85,7 +85,7 @@ var (
// rootServer is the root gRPC server.
//
// Each gRPC service registers itself to this server during init().
// Each gRPC service registers itself to this server during start().
rootServer *grpc.Server
// env is the default environment.

@ -265,7 +265,7 @@ dependencies:
- name: subchart1
repository: http://localhost:10191
version: 0.1.0
condition: subchart1.enabled, global.subchart1.enabled
condition: subchart1.enabled,global.subchart1.enabled
tags:
- front-end
- subchart1
@ -303,7 +303,6 @@ The `--set` parameter can be used as usual to alter tag and condition values.
````
helm install --set tags.front-end=true --set subchart2.enabled=false
````
##### Tags and Condition Resolution

@ -106,6 +106,43 @@ For example:
The above will render the template when .Values.foo is defined, but will fail
to render and exit when .Values.foo is undefined.
## Using the 'tpl' Function
The `tpl` function allows developers to evaluate strings as templates inside a template.
This is useful to pass a template string as a value to a chart or render external configuration files.
Syntax: `{{ tpl TEMPLATE_STRING VALUES }}`
Examples:
```
# values
template: "{{ .Values.name }}"
name: "Tom"
# template
{{ tpl .Values.template . }}
# output
Tom
```
Rendering a external configuration file:
```
# external configuration file conf/app.conf
firstName={{ .Values.firstName }}
lastName={{ .Values.lastName }}
# values
firstName: Peter
lastName: Parker
# template
{{ tpl (.Files.Get "conf/app.conf") . }}
# output
firstName=Peter
lastName=Parker
```
## Creating Image Pull Secrets
Image pull secrets are essentially a combination of _registry_, _username_, and _password_. You may need them in an application you are deploying, but to create them requires running _base64_ a couple of times. We can write a helper template to compose the Docker configuration file for use as the Secret's payload. Here is an example:

@ -14,6 +14,5 @@ It simply deploys a single pod running Alpine Linux.
The `nginx` chart shows how to compose several resources into one chart,
and it illustrates more complex template usage.
It deploys a `deployment` (which creates a `replica set`), a `config
map`, and a `service`. The replica set starts an nginx pod. The config
It deploys a `Deployment` (which creates a `ReplicaSet`), a `ConfigMap`, and a `Service`. The replica set starts an nginx pod. The config
map stores the files that the nginx server can serve.

@ -49,6 +49,7 @@ helm list [flags] [FILTER]
-m, --max int maximum number of releases to fetch (default 256)
--namespace string show releases within a specific namespace
-o, --offset string next release name in the list, used to offset from start value
--output string output the specified format (json or yaml)
--pending show pending releases
-r, --reverse reverse the sort order
-q, --short output short (quiet) listing format
@ -73,4 +74,4 @@ helm list [flags] [FILTER]
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 8-Mar-2018
###### Auto generated by spf13/cobra on 17-Apr-2018

@ -71,7 +71,7 @@ metadata:
name: tiller-manager
namespace: tiller-world
rules:
- apiGroups: ["", "extensions", "apps"]
- apiGroups: ["", "batch", "extensions", "apps"]
resources: ["*"]
verbs: ["*"]
```

@ -16,6 +16,7 @@ or [pull request](https://github.com/kubernetes/helm/pulls).
- [GitLab, Consumer Driven Contracts, Helm and Kubernetes](https://medium.com/@enxebre/gitlab-consumer-driven-contracts-helm-and-kubernetes-b7235a60a1cb#.xwp1y4tgi)
- [Writing a Helm Chart](https://www.influxdata.com/packaged-kubernetes-deployments-writing-helm-chart/)
- [Creating a Helm Plugin in 3 Steps](http://technosophos.com/2017/03/21/creating-a-helm-plugin.html)
- [Awesome Helm](https://github.com/cdwv/awesome-helm) - List of awesome Helm resources
## Video, Audio, and Podcast

@ -2,9 +2,36 @@
**IMPORTANT**: If your experience deviates from this document, please document the changes to keep it up-to-date.
## Release Meetings
As part of the release process, two of the weekly developer calls will be co-opted
as "release meetings."
### Start of the Release Cycle
The first developer call after a release will be used as the release meeting to
start the next release cycle. During this meeting, the following items must be
identified:
- Release date
- Goals/Objectives for this release
- The release manager (basically whoever is going to cut the release)
- Any other important details for the community
All of this information should be added to the GitHub milestone for the given
release. This should give the community and maintainers a clear set of guidelines
to follow when choosing whether or not to add issues and PRs to a given release.
### End (almost) of the Release Cycle
The developer call closest to two weeks before the scheduled release date will
be used to review any remaining PRs that should be pulled into the release. This
is the place to debate whether or not we should wait before cutting a release and
any other concerns. At the end of this meeting, if the release date has not been
pushed out, the first RC should be cut. Subsequent developer calls in between this
meeting and the release date should have some time set aside to see if any bugs
were found. Once the release date is reached, the final release can be cut
## A Maintainer's Guide to Releasing Helm
So you're in charge of a new release for helm? Cool. Here's what to do...
So you're in charge of a new release for Helm? Cool. Here's what to do...
![TODO: Nothing](images/nothing.png)

@ -43,7 +43,7 @@ carefully curated and maintained charts. This chart repository is named
You can see which charts are available by running `helm search`:
```
```console
$ helm search
NAME VERSION DESCRIPTION
stable/drupal 0.3.2 One of the most versatile open source content m...
@ -56,7 +56,7 @@ stable/mysql 0.1.0 Chart for MySQL
With no filter, `helm search` shows you all of the available charts. You
can narrow down your results by searching with a filter:
```
```console
$ helm search mysql
NAME VERSION DESCRIPTION
stable/mysql 0.1.0 Chart for MySQL
@ -69,7 +69,7 @@ Why is
`mariadb` in the list? Because its package description relates it to
MySQL. We can use `helm inspect chart` to see this:
```
```console
$ helm inspect stable/mariadb
Fetched stable/mariadb to mariadb-0.5.1.tgz
description: Chart for MariaDB
@ -91,7 +91,7 @@ package you want to install, you can use `helm install` to install it.
To install a new package, use the `helm install` command. At its
simplest, it takes only one argument: The name of the chart.
```
```console
$ helm install stable/mariadb
Fetched stable/mariadb-0.3.0 to /Users/mattbutcher/Code/Go/src/k8s.io/helm/mariadb-0.3.0.tgz
happy-panda
@ -139,7 +139,7 @@ may take a long time to install into the cluster.
To keep track of a release's state, or to re-read configuration
information, you can use `helm status`:
```
```console
$ helm status happy-panda
Last Deployed: Wed Sep 28 12:32:28 2016
Namespace: default
@ -392,14 +392,14 @@ is not a full list of cli flags. To see a description of all flags, just run
When it is time to uninstall or delete a release from the cluster, use
the `helm delete` command:
```
```console
$ helm delete happy-panda
```
This will remove the release from the cluster. You can see all of your
currently deployed releases with the `helm list` command:
```
```console
$ helm list
NAME VERSION UPDATED STATUS CHART
inky-cat 1 Wed Sep 28 12:59:46 2016 DEPLOYED alpine-0.1.0

@ -129,8 +129,8 @@ kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app: {{ template "<CHARTNAME>.name" . }}
chart: {{ template "<CHARTNAME>.chart" . }}
app: {{ include "<CHARTNAME>.name" . }}
chart: {{ include "<CHARTNAME>.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
{{- with .Values.ingress.annotations }}
@ -164,22 +164,22 @@ spec:
const defaultDeployment = `apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ template "<CHARTNAME>.fullname" . }}
name: {{ include "<CHARTNAME>.fullname" . }}
labels:
app: {{ template "<CHARTNAME>.name" . }}
chart: {{ template "<CHARTNAME>.chart" . }}
app: {{ include "<CHARTNAME>.name" . }}
chart: {{ include "<CHARTNAME>.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ template "<CHARTNAME>.name" . }}
app: {{ include "<CHARTNAME>.name" . }}
release: {{ .Release.Name }}
template:
metadata:
labels:
app: {{ template "<CHARTNAME>.name" . }}
app: {{ include "<CHARTNAME>.name" . }}
release: {{ .Release.Name }}
spec:
containers:
@ -217,10 +217,10 @@ spec:
const defaultService = `apiVersion: v1
kind: Service
metadata:
name: {{ template "<CHARTNAME>.fullname" . }}
name: {{ include "<CHARTNAME>.fullname" . }}
labels:
app: {{ template "<CHARTNAME>.name" . }}
chart: {{ template "<CHARTNAME>.chart" . }}
app: {{ include "<CHARTNAME>.name" . }}
chart: {{ include "<CHARTNAME>.chart" . }}
release: {{ .Release.Name }}
heritage: {{ .Release.Service }}
spec:
@ -231,7 +231,7 @@ spec:
protocol: TCP
name: http
selector:
app: {{ template "<CHARTNAME>.name" . }}
app: {{ include "<CHARTNAME>.name" . }}
release: {{ .Release.Name }}
`
@ -241,16 +241,16 @@ const defaultNotes = `1. Get the application URL by running these commands:
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "<CHARTNAME>.fullname" . }})
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "<CHARTNAME>.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "<CHARTNAME>.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "<CHARTNAME>.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
You can watch the status of by running 'kubectl get svc -w {{ include "<CHARTNAME>.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "<CHARTNAME>.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "<CHARTNAME>.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ include "<CHARTNAME>.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:80
{{- end }}

@ -16,11 +16,21 @@ limitations under the License.
package getter
import (
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strconv"
"testing"
)
type TestFileHandler struct{}
func (h *TestFileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
HandleClient(w, r)
}
func TestHTTPGetter(t *testing.T) {
g, err := newHTTPGetter("http://example.com", "", "", "")
if err != nil {
@ -56,3 +66,42 @@ func TestHTTPGetter(t *testing.T) {
t.Fatal("Expected newHTTPGetter to return a non-default HTTP client")
}
}
func HandleClient(writer http.ResponseWriter, request *http.Request) {
f, _ := os.Open("testdata/sssd-0.1.0.tgz")
defer f.Close()
b := make([]byte, 512)
f.Read(b)
//Get the file size
FileStat, _ := f.Stat()
FileSize := strconv.FormatInt(FileStat.Size(), 10)
//Simulating improper header values from bitbucket
writer.Header().Set("Content-Type", "application/x-tar")
writer.Header().Set("Content-Encoding", "gzip")
writer.Header().Set("Content-Length", FileSize)
f.Seek(0, 0)
io.Copy(writer, f)
return
}
func TestHTTPGetterTarDownload(t *testing.T) {
h := &TestFileHandler{}
server := httptest.NewServer(h)
defer server.Close()
g, err := newHTTPGetter(server.URL, "", "", "")
if err != nil {
t.Fatal(err)
}
data, _ := g.Get(server.URL)
mimeType := http.DetectContentType(data.Bytes())
expectedMimeType := "application/x-gzip"
if mimeType != expectedMimeType {
t.Fatalf("Expected response with MIME type %s, but got %s", expectedMimeType, mimeType)
}
}

Binary file not shown.

@ -49,9 +49,28 @@ var _ Interface = (*FakeClient)(nil)
// ListReleases lists the current releases
func (c *FakeClient) ListReleases(opts ...ReleaseListOption) (*rls.ListReleasesResponse, error) {
reqOpts := c.Opts
for _, opt := range opts {
opt(&reqOpts)
}
req := &reqOpts.listReq
rels := c.Rels
count := int64(len(c.Rels))
var next string
limit := req.GetLimit()
// TODO: Handle all other options.
if limit != 0 && limit < count {
rels = rels[:limit]
count = limit
next = c.Rels[limit].GetName()
}
resp := &rls.ListReleasesResponse{
Count: int64(len(c.Rels)),
Releases: c.Rels,
Count: count,
Releases: rels,
}
if next != "" {
resp.Next = next
}
return resp, nil
}

@ -40,7 +40,16 @@ func getNamespace(client internalclientset.Interface, namespace string) (*core.N
func ensureNamespace(client internalclientset.Interface, namespace string) error {
_, err := getNamespace(client, namespace)
if err != nil && errors.IsNotFound(err) {
return createNamespace(client, namespace)
err = createNamespace(client, namespace)
// If multiple commands which run `ensureNamespace` are run in
// parallel, then protect against the race condition in which
// the namespace did not exist when `getNamespace` was executed,
// but did exist when `createNamespace` was executed. If that
// happens, we can just proceed as normal.
if errors.IsAlreadyExists(err) {
return nil
}
}
return err
}

@ -163,7 +163,7 @@ func (s *Storage) History(name string) ([]*rspb.Release, error) {
return s.Driver.Query(map[string]string{"NAME": name, "OWNER": "TILLER"})
}
// removeLeastRecent removes items from history until the lengh number of releases
// removeLeastRecent removes items from history until the length number of releases
// does not exceed max.
//
// We allow max to be set explicitly so that calling functions can "make space"

@ -325,6 +325,11 @@ func inMap(k rune, m map[rune]bool) bool {
func typedVal(v []rune, st bool) interface{} {
val := string(v)
if st {
return val
}
if strings.EqualFold(val, "true") {
return true
}
@ -337,8 +342,8 @@ func typedVal(v []rune, st bool) interface{} {
return nil
}
// If this value does not start with zero, and not returnString, try parsing it to an int
if !st && len(val) != 0 && val[0] != '0' {
// If this value does not start with zero, try parsing it to an int
if len(val) != 0 && val[0] != '0' {
if iv, err := strconv.ParseInt(val, 10, 64); err == nil {
return iv
}

@ -75,6 +75,16 @@ func TestParseSet(t *testing.T) {
expect: map[string]interface{}{"long_int_string": "1234567890"},
err: false,
},
{
str: "boolean=true",
expect: map[string]interface{}{"boolean": "true"},
err: false,
},
{
str: "is_null=null",
expect: map[string]interface{}{"is_null": "null"},
err: false,
},
}
tests := []struct {
str string
@ -117,6 +127,15 @@ func TestParseSet(t *testing.T) {
str: "long_int=1234567890",
expect: map[string]interface{}{"long_int": 1234567890},
},
{
str: "boolean=true",
expect: map[string]interface{}{"boolean": true},
},
{
str: "is_null=null",
expect: map[string]interface{}{"is_null": nil},
err: false,
},
{
str: "name1,name2=",
err: true,
@ -336,12 +355,13 @@ func TestParseInto(t *testing.T) {
"inner2": "value2",
},
}
input := "outer.inner1=value1,outer.inner3=value3"
input := "outer.inner1=value1,outer.inner3=value3,outer.inner4=4"
expect := map[string]interface{}{
"outer": map[string]interface{}{
"inner1": "value1",
"inner2": "value2",
"inner3": "value3",
"inner4": 4,
},
}
@ -362,6 +382,39 @@ func TestParseInto(t *testing.T) {
t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2)
}
}
func TestParseIntoString(t *testing.T) {
got := map[string]interface{}{
"outer": map[string]interface{}{
"inner1": "overwrite",
"inner2": "value2",
},
}
input := "outer.inner1=1,outer.inner3=3"
expect := map[string]interface{}{
"outer": map[string]interface{}{
"inner1": "1",
"inner2": "value2",
"inner3": "3",
},
}
if err := ParseIntoString(input, got); err != nil {
t.Fatal(err)
}
y1, err := yaml.Marshal(expect)
if err != nil {
t.Fatal(err)
}
y2, err := yaml.Marshal(got)
if err != nil {
t.Fatalf("Error serializing parsed value: %s", err)
}
if string(y1) != string(y2) {
t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2)
}
}
func TestToYAML(t *testing.T) {
// The TestParse does the hard part. We just verify that YAML formatting is

@ -98,8 +98,6 @@ type Engine interface {
type KubeClient interface {
// Create creates one or more resources.
//
// namespace must contain a valid existing namespace.
//
// reader must contain a YAML stream (one or more YAML documents separated
// by "\n---\n").
Create(namespace string, reader io.Reader, timeout int64, shouldWait bool) error

@ -190,7 +190,7 @@ func (s *ReleaseServer) uniqName(start string, reuse bool) (string, error) {
s.Log("name %s exists but is not in use, reusing name", start)
return start, nil
} else if reuse {
return "", errors.New("cannot re-use a name that is still in use")
return "", fmt.Errorf("a released named %s is in use, cannot re-use a name that is still in use", start)
}
return "", fmt.Errorf("a release named %s already exists.\nRun: helm ls --all %s; to check the status of the release\nOr run: helm del --purge %s; to delete it", start, start, start)

Loading…
Cancel
Save