Commit 33ca00f8 authored by jackgr's avatar jackgr

Add GetChart method

parent f8193c25
......@@ -246,7 +246,7 @@ func compareContent(filename, content string) error {
compare := base64.StdEncoding.EncodeToString(b)
if content != compare {
return fmt.Errorf("Expected member content\n%s\ngot\n%s", compare, content)
return fmt.Errorf("Expected member content\n%v\ngot\n%v", []byte(compare), []byte(content))
}
return nil
......
......@@ -25,6 +25,7 @@ import (
"fmt"
"net/http"
"net/url"
"regexp"
)
......@@ -43,7 +44,7 @@ type GCSRepo struct {
// URLFormatMatcher matches the GCS URL format (gs:).
var URLFormatMatcher = regexp.MustCompile("gs://(.*)")
var GCSRepoFormat = common.RepoFormat(fmt.Sprintf("%s;%s", common.VersionedRepo, common.OneLevelRepo))
var GCSRepoFormat = common.RepoFormat(fmt.Sprintf("%s;%s", common.UnversionedRepo, common.OneLevelRepo))
// NewGCSRepo creates a GCS repository.
func NewGCSRepo(name, URL string, httpClient *http.Client) (*GCSRepo, error) {
......@@ -106,8 +107,10 @@ func (g *GCSRepo) ListCharts(regex *regexp.Regexp) ([]string, error) {
continue
}
if regex == nil || regex.MatchString(object.Name) {
charts = append(charts, object.Name)
}
}
if pageToken = res.NextPageToken; pageToken == "" {
break
......@@ -117,11 +120,38 @@ func (g *GCSRepo) ListCharts(regex *regexp.Regexp) ([]string, error) {
return charts, nil
}
// TODO: Implement GetChart.
// GetChart retrieves, unpacks and returns a chart by name.
func (g *GCSRepo) GetChart(name string) (*chart.Chart, error) {
return nil, fmt.Errorf("not implemented: GCSRepo.GetChart")
// Charts should be named bucket/chart-X.Y.Z.tgz, so tease apart the version here
if !ChartNameMatcher.MatchString(name) {
return nil, fmt.Errorf("name must be of the form <name>-<version>.tgz, was %s", name)
}
call := g.service.Objects.Get(g.bucket, name)
object, err := call.Do()
if err != nil {
return nil, err
}
u, err := url.Parse(object.MediaLink)
if err != nil {
return nil, fmt.Errorf("Cannot parse URL %s for chart %s/%s: %s",
object.MediaLink, object.Bucket, object.Name, err)
}
getter := util.NewHTTPClient(3, g.httpClient, util.NewSleeper())
body, code, err := getter.Get(u.String())
if err != nil {
return nil, fmt.Errorf("Cannot fetch URL %s for chart %s/%s: %d %s",
object.MediaLink, object.Bucket, object.Name, code, err)
}
return chart.Load(body)
}
// Do performs an HTTP operation on the receiver's httpClient.
func (g *GCSRepo) Do(req *http.Request) (resp *http.Response, err error) {
return g.httpClient.Do(req)
}
// TODO: Remove GetShortURL when no longer needed.
......
......@@ -17,11 +17,23 @@ limitations under the License.
package repo
import (
"github.com/kubernetes/helm/pkg/chart"
"github.com/kubernetes/helm/pkg/common"
"os"
"reflect"
"regexp"
"testing"
)
var (
TestArchiveBucket = os.Getenv("TEST_ARCHIVE_BUCKET")
TestArchiveName = "frobnitz-0.0.1.tgz"
TestChartFile = "testdata/frobnitz/Chart.yaml"
TestShouldFindRegex = regexp.MustCompile(TestArchiveName)
TestShouldNotFindRegex = regexp.MustCompile("foobar")
)
func TestValidGSURL(t *testing.T) {
var validURL = "gs://bucket"
tr, err := NewGCSRepo("testName", validURL, nil)
......@@ -50,3 +62,94 @@ func TestInvalidGSURL(t *testing.T) {
t.Fatalf("expected error did not occur for invalid URL")
}
}
func TestListCharts(t *testing.T) {
if TestArchiveBucket != "" {
tr, err := NewGCSRepo("testName", TestArchiveBucket, nil)
if err != nil {
t.Fatal(err)
}
charts, err := tr.ListCharts(nil)
if err != nil {
t.Fatal(err)
}
if len(charts) != 1 {
t.Fatalf("expected one chart in test bucket, got %d", len(charts))
}
name := charts[0]
if name != TestArchiveName {
t.Fatalf("expected chart named %s in test bucket, got %s", TestArchiveName, name)
}
}
}
func TestListChartsWithShouldFindRegex(t *testing.T) {
if TestArchiveBucket != "" {
tr, err := NewGCSRepo("testName", TestArchiveBucket, nil)
if err != nil {
t.Fatal(err)
}
charts, err := tr.ListCharts(TestShouldFindRegex)
if err != nil {
t.Fatal(err)
}
if len(charts) != 1 {
t.Fatalf("expected one chart to match regex, got %d", len(charts))
}
}
}
func TestListChartsWithShouldNotFindRegex(t *testing.T) {
if TestArchiveBucket != "" {
tr, err := NewGCSRepo("testName", TestArchiveBucket, nil)
if err != nil {
t.Fatal(err)
}
charts, err := tr.ListCharts(TestShouldNotFindRegex)
if err != nil {
t.Fatal(err)
}
if len(charts) != 0 {
t.Fatalf("expected zero charts to match regex, got %d", len(charts))
}
}
}
func TestGetChart(t *testing.T) {
if TestArchiveBucket != "" {
tr, err := NewGCSRepo("testName", TestArchiveBucket, nil)
if err != nil {
t.Fatal(err)
}
tc, err := tr.GetChart(TestArchiveName)
if err != nil {
t.Fatal(err)
}
have := tc.Chartfile()
want, err := chart.LoadChartfile(TestChartFile)
if err != nil {
t.Fatal(err)
}
if reflect.DeepEqual(want, have) {
t.Fatalf("retrieved an invalid chart\nwant:%#v\nhave:\n%#v\n", want, have)
}
}
}
func TestGetChartWithInvalidName(t *testing.T) {
var invalidURL = "https://bucket"
_, err := NewGCSRepo("testName", invalidURL, nil)
if err == nil {
t.Fatalf("expected error did not occur for invalid URL")
}
}
The testdata directory here holds charts that match the specification.
The `fromnitz/` directory contains a chart that matches the chart
specification.
The `frobnitz-0.0.1.tgz` file is an archive of the `frobnitz` directory.
#helm:generate foo
name: frobnitz
description: This is a frobniz.
version: "1.2.3-alpha.1+12345"
keywords:
- frobnitz
- sprocket
- dodad
maintainers:
- name: The Helm Team
email: helm@example.com
- name: Someone Else
email: nobody@example.com
source:
- https://example.com/foo/bar
home: http://example.com
dependencies:
- name: thingerbob
location: https://example.com/charts/thingerbob-3.2.1.tgz
version: ^3
environment:
- name: Kubernetes
version: ~1.1
extensions:
- extensions/v1beta1
- extensions/v1beta1/daemonset
apiGroups:
- 3rdParty
expander:
name: Expandybird
entrypoint: templates/wordpress.jinja
schema: wordpress.jinja.schema
\ No newline at end of file
# Frobnitz
This is an example chart.
## Usage
This is an example. It has no usage.
## Development
For developer info, see the top-level repository.
This is a placeholder for documentation.
<?xml version="1.0"?>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.0" width="256" height="256" id="test">
<desc>Example icon</desc>
<rect id="first" x="2" y="2" width="40" height="60" fill="navy"/>
<rect id="second" x="15" y="4" width="40" height="60" fill="red"/>
</svg>
# Google Cloud Deployment Manager template
resources:
- name: nfs-disk
type: compute.v1.disk
properties:
zone: us-central1-b
sizeGb: 200
- name: mysql-disk
type: compute.v1.disk
properties:
zone: us-central1-b
sizeGb: 200
#helm:generate dm_template
{% set PROPERTIES = properties or {} %}
{% set PROJECT = PROPERTIES['project'] or 'dm-k8s-testing' %}
{% set NFS_SERVER = PROPERTIES['nfs-server'] or {} %}
{% set NFS_SERVER_IP = NFS_SERVER['ip'] or '10.0.253.247' %}
{% set NFS_SERVER_PORT = NFS_SERVER['port'] or 2049 %}
{% set NFS_SERVER_DISK = NFS_SERVER['disk'] or 'nfs-disk' %}
{% set NFS_SERVER_DISK_FSTYPE = NFS_SERVER['fstype'] or 'ext4' %}
{% set NGINX = PROPERTIES['nginx'] or {} %}
{% set NGINX_PORT = 80 %}
{% set NGINX_REPLICAS = NGINX['replicas'] or 2 %}
{% set WORDPRESS_PHP = PROPERTIES['wordpress-php'] or {} %}
{% set WORDPRESS_PHP_REPLICAS = WORDPRESS_PHP['replicas'] or 2 %}
{% set WORDPRESS_PHP_PORT = WORDPRESS_PHP['port'] or 9000 %}
{% set MYSQL = PROPERTIES['mysql'] or {} %}
{% set MYSQL_PORT = MYSQL['port'] or 3306 %}
{% set MYSQL_PASSWORD = MYSQL['password'] or 'mysql-password' %}
{% set MYSQL_DISK = MYSQL['disk'] or 'mysql-disk' %}
{% set MYSQL_DISK_FSTYPE = MYSQL['fstype'] or 'ext4' %}
resources:
- name: nfs
type: github.com/kubernetes/application-dm-templates/storage/nfs:v1
properties:
ip: {{ NFS_SERVER_IP }}
port: {{ NFS_SERVER_PORT }}
disk: {{ NFS_SERVER_DISK }}
fstype: {{NFS_SERVER_DISK_FSTYPE }}
- name: nginx
type: github.com/kubernetes/application-dm-templates/common/replicatedservice:v2
properties:
service_port: {{ NGINX_PORT }}
container_port: {{ NGINX_PORT }}
replicas: {{ NGINX_REPLICAS }}
external_service: true
image: gcr.io/{{ PROJECT }}/nginx:latest
volumes:
- mount_path: /var/www/html
persistentVolumeClaim:
claimName: nfs
- name: mysql
type: github.com/kubernetes/application-dm-templates/common/replicatedservice:v2
properties:
service_port: {{ MYSQL_PORT }}
container_port: {{ MYSQL_PORT }}
replicas: 1
image: mysql:5.6
env:
- name: MYSQL_ROOT_PASSWORD
value: {{ MYSQL_PASSWORD }}
volumes:
- mount_path: /var/lib/mysql
gcePersistentDisk:
pdName: {{ MYSQL_DISK }}
fsType: {{ MYSQL_DISK_FSTYPE }}
- name: wordpress-php
type: github.com/kubernetes/application-dm-templates/common/replicatedservice:v2
properties:
service_name: wordpress-php
service_port: {{ WORDPRESS_PHP_PORT }}
container_port: {{ WORDPRESS_PHP_PORT }}
replicas: 2
image: wordpress:fpm
env:
- name: WORDPRESS_DB_PASSWORD
value: {{ MYSQL_PASSWORD }}
- name: WORDPRESS_DB_HOST
value: mysql-service
volumes:
- mount_path: /var/www/html
persistentVolumeClaim:
claimName: nfs
info:
title: Wordpress
description: |
Defines a Wordpress website by defining four replicated services: an NFS service, an nginx service, a wordpress-php service, and a MySQL service.
The nginx service and the Wordpress-php service both use NFS to share files.
properties:
project:
type: string
default: dm-k8s-testing
description: Project location to load the images from.
nfs-service:
type: object
properties:
ip:
type: string
default: 10.0.253.247
description: The IP of the NFS service.
port:
type: int
default: 2049
description: The port of the NFS service.
disk:
type: string
default: nfs-disk
description: The name of the persistent disk the NFS service uses.
fstype:
type: string
default: ext4
description: The filesystem the disk of the NFS service uses.
nginx:
type: object
properties:
replicas:
type: int
default: 2
description: The number of replicas for the nginx service.
wordpress-php:
type: object
properties:
replicas:
type: int
default: 2
description: The number of replicas for the wordpress-php service.
port:
type: int
default: 9000
description: The port the wordpress-php service runs on.
mysql:
type: object
properties:
port:
type: int
default: 3306
description: The port the MySQL service runs on.
password:
type: string
default: mysql-password
description: The root password of the MySQL service.
disk:
type: string
default: mysql-disk
description: The name of the persistent disk the MySQL service uses.
fstype:
type: string
default: ext4
description: The filesystem the disk of the MySQL service uses.
imports:
- path: wordpress.jinja
resources:
- name: wordpress
type: wordpress.jinja
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