fix(deploy): add tests

pull/291/head
Matt Butcher 9 years ago
parent 4a5cf501b1
commit bbb3229da1

@ -2,10 +2,15 @@ package main
import (
"errors"
"fmt"
"os"
"regexp"
"strings"
"github.com/aokoli/goutils"
"github.com/codegangsta/cli"
dep "github.com/deis/helm-dm/deploy"
"github.com/deis/helm-dm/dm"
"github.com/deis/helm-dm/format"
"github.com/kubernetes/deployment-manager/chart"
)
@ -93,7 +98,12 @@ func doDeploy(cfg *dep.Deployment, host string, dry bool) error {
if err != nil {
return err
}
if cfg.Name == "" {
cfg.Name = genName(c.Chartfile().Name)
}
// TODO: Is it better to generate the file in temp dir like this, or
// just put it in the CWD?
//tdir, err := ioutil.TempDir("", "helm-")
//if err != nil {
//format.Warn("Could not create temporary directory. Using .")
@ -107,18 +117,36 @@ func doDeploy(cfg *dep.Deployment, host string, dry bool) error {
return err
}
cfg.Filename = tfile
} else if cfg.Name == "" {
n, _, e := parseTarName(cfg.Filename)
if e != nil {
return e
}
cfg.Name = n
}
if !dry {
if err := uploadTar(cfg.Filename); err != nil {
return err
}
if dry {
format.Info("Prepared deploy %q using file %q", cfg.Name, cfg.Filename)
return nil
}
return nil
c := dm.NewClient(host)
return c.DeployChart(cfg.Filename, cfg.Name)
}
func uploadTar(filename string) error {
return nil
func genName(pname string) string {
s, _ := goutils.RandomAlphaNumeric(8)
return fmt.Sprintf("%s-%s", pname, s)
}
func parseTarName(name string) (string, string, error) {
tnregexp := regexp.MustCompile(chart.TarNameRegex)
if strings.HasSuffix(name, ".tgz") {
name = strings.TrimSuffix(name, ".tgz")
}
v := tnregexp.FindStringSubmatch(name)
if v == nil {
return name, "", fmt.Errorf("invalid name %s", name)
}
return v[1], v[2], nil
}

@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"os"
"path"
"path/filepath"
"strings"
"time"
@ -178,7 +179,8 @@ func (c *Client) DeployChart(filename, deployname string) error {
}
defer f.Close()
request, err := http.NewRequest("POST", "/v2/deployments/", f)
u, err := c.url("/v2/deployments")
request, err := http.NewRequest("POST", u, f)
// There is an argument to be made for using the legacy x-octet-stream for
// this. But since we control both sides, we should use the standard one.
@ -192,7 +194,7 @@ func (c *Client) DeployChart(filename, deployname string) error {
client := http.Client{
Timeout: time.Duration(time.Duration(DefaultHTTPTimeout) * time.Second),
Transport: c.Transport,
Transport: c.transport(),
}
response, err := client.Do(request)
@ -200,17 +202,14 @@ func (c *Client) DeployChart(filename, deployname string) error {
return err
}
body, err := ioutil.ReadAll(response.Body)
response.Body.Close()
if err != nil {
return err
}
// FIXME: We only want 200 OK or 204(?) CREATED
if response.StatusCode < http.StatusOK ||
response.StatusCode >= http.StatusMultipleChoices {
message := fmt.Sprintf("status code: %d status: %s : %s", response.StatusCode, response.Status, body)
return fmt.Errorf("Failed to post: %s", message)
// We only want 201 CREATED. Admittedly, we could accept 200 and 202.
if response.StatusCode < http.StatusCreated {
body, err := ioutil.ReadAll(response.Body)
response.Body.Close()
if err != nil {
return err
}
return fmt.Errorf("Failed to post: %d %s - %s", response.StatusCode, response.Status, body)
}
return nil
@ -219,7 +218,7 @@ func (c *Client) DeployChart(filename, deployname string) error {
// GetDeployment retrieves the supplied deployment
func (c *Client) GetDeployment(name string) (*common.Deployment, error) {
var deployment *common.Deployment
if err := c.CallService(filepath.Join("deployments", name), "GET", "get deployment", &deployment, nil); err != nil {
if err := c.CallService(path.Join("deployments", name), "GET", "get deployment", &deployment, nil); err != nil {
return nil, err
}
return deployment, nil

@ -1,8 +1,10 @@
package dm
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
@ -130,3 +132,40 @@ func TestGetDeployment(t *testing.T) {
t.Fatalf("expected deployment status 'Deployed', got '%s'", d.State.Status)
}
}
func TestDeployChart(t *testing.T) {
testfile := "../testdata/charts/frobnitz-0.0.1.tgz"
testname := "sparkles"
fi, err := os.Stat(testfile)
if err != nil {
t.Fatalf("could not stat file %s: %s", testfile, err)
}
expectedSize := int(fi.Size())
fc := &fakeClient{
handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Errorf("Failed to read data off of request: %s", err)
}
if len(data) != expectedSize {
t.Errorf("Expected content length %d, got %d", expectedSize, len(data))
}
if cn := r.Header.Get("x-chart-name"); cn != "frobnitz-0.0.1.tgz" {
t.Errorf("Expected frobnitz-0.0.1.tgz, got %q", cn)
}
if dn := r.Header.Get("x-deployment-name"); dn != "sparkles" {
t.Errorf("Expected sparkles, got %q", dn)
}
w.WriteHeader(201)
}),
}
defer fc.teardown()
if err := fc.setup().DeployChart(testfile, testname); err != nil {
t.Fatal(err)
}
}

58
glide.lock generated

@ -1,61 +1,21 @@
hash: 71e9a5a2d1f27a0ecfa70595154f87fee9f5519ba6bdea4d01834bab5aa29074
updated: 2016-02-06T17:26:31.257079434-08:00
hash: c54a5f678965132636637023cd71e4d4065360b229242718f7f6e7674ac8d1c1
updated: 2016-02-09T16:35:44.56035745-07:00
imports:
- name: github.com/aokoli/goutils
version: 9c37978a95bd5c709a15883b6242714ea6709e64
- name: github.com/codegangsta/cli
version: cf1f63a7274872768d4037305d572b70b1199397
- name: github.com/emicklei/go-restful
version: b86acf97a74ed7603ac78d012f5535b4d587b156
version: 8cea2901d4b2c28b97001e67a7d2d60e227f3da6
- name: github.com/ghodss/yaml
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
- name: github.com/golang/glog
version: 23def4e6c14b4da8ac2ed8007337bc5eb5007998
- name: github.com/golang/protobuf
version: 45bba206dd5270d96bac4942dcfe515726613249
- name: github.com/google/go-github
version: b8b4ac742977310ff6e75140a403a38dab109977
- name: github.com/google/go-querystring
version: 2a60fc2ba6c19de80291203597d752e9ba58e4c0
- name: github.com/gorilla/context
version: 1c83b3eabd45b6d76072b66b746c20815fb2872d
- name: github.com/gorilla/handlers
version: b3aff83722cb2ae031a70cae984650e3a16cd20e
- name: github.com/gorilla/mux
version: 26a6070f849969ba72b72256e9f14cf519751690
- name: github.com/gorilla/schema
version: 14c555599c2a4f493c1e13fd1ea6fdf721739028
- name: github.com/kubernetes/deployment-manager
version: ""
version: 4fe37ea6342991e4d0519e48d1fd6fca061baf5c
subpackages:
- /common
- chart
- common
- log
- name: github.com/Masterminds/semver
version: c4f7ef0702f269161a60489ccbbc9f1241ad1265
- name: github.com/mjibson/appstats
version: 0542d5f0e87ea3a8fa4174322b9532f5d04f9fa8
- name: golang.org/x/crypto
version: 1f22c0103821b9390939b6776727195525381532
- name: golang.org/x/net
version: 6c581b96a7d38dd755f986fcf4f29665597694c0
- name: golang.org/x/oauth2
version: 8a57ed94ffd43444c0879fe75701732a38afc985
- name: golang.org/x/text
version: 5aaa1a807bf8a2f763540b140e7805973476eb88
- name: google.golang.com/appengine/datastore
version: ""
repo: https://google.golang.com/appengine/datastore
- name: google.golang.com/appengine/memcache
version: ""
repo: https://google.golang.com/appengine/memcache
- name: google.golang.com/appengine/user
version: ""
repo: https://google.golang.com/appengine/user
- name: google.golang.org/api
version: 8fa1015948e6fc21c025050624e4c4e2f4f405c4
- name: google.golang.org/appengine
version: 6bde959377a90acb53366051d7d587bfd7171354
- name: google.golang.org/cloud
version: 5a3b06f8b5da3b7c3a93da43163b872c86c509ef
- name: google.golang.org/grpc
version: 5d64098b94ee9dbbea8ddc130208696bcd199ba4
- name: gopkg.in/yaml.v2
version: f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
devImports: []

@ -8,3 +8,4 @@ import:
- /common
- package: github.com/ghodss/yaml
- package: github.com/Masterminds/semver
- package: github.com/aokoli/goutils

@ -0,0 +1,9 @@
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.
The `ill` chart and directory is a chart that is not 100% compatible,
but which should still be parseable.

Binary file not shown.

@ -0,0 +1,27 @@
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
version: ^3
location: https://example.com/charts/thingerbob-3.2.1.tgz
environment:
- name: Kubernetes
version: ~1.1
extensions:
- extensions/v1beta1
- extensions/v1beta1/daemonset
apiGroups:
- 3rdParty

@ -0,0 +1 @@
THIS IS PLACEHOLDER TEXT.

@ -0,0 +1,11 @@
# Frobnitz
This is an example chart.
## Usage
This is an example. It has no usage.
## Development
For developer info, see the top-level repository.

@ -0,0 +1 @@
This is a placeholder for documentation.

@ -0,0 +1,8 @@
<?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>

After

Width:  |  Height:  |  Size: 374 B

@ -0,0 +1,12 @@
# 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

@ -0,0 +1,72 @@
#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

@ -0,0 +1,69 @@
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.

@ -0,0 +1,6 @@
imports:
- path: wordpress.jinja
resources:
- name: wordpress
type: wordpress.jinja
Loading…
Cancel
Save