diff --git a/cmd/deploy.go b/cmd/deploy.go index 8126ca4e4..80faeea6d 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -1,21 +1,35 @@ package main import ( + "encoding/json" "errors" dep "github.com/deis/helm-dm/deploy" "github.com/deis/helm-dm/format" ) -func deploy(cfg *dep.Deployment, dry bool) error { - if dry { - format.Error("Not implemented: --dry-run") - } +func deploy(cfg *dep.Deployment, host string, dry bool) error { if cfg.Filename == "" { return errors.New("A filename must be specified. For a tar archive, this is the name of the root template in the archive.") } - if err := cfg.Commit(); err != nil { + if err := cfg.Prepare(); err != nil { + format.Error("Failed to prepare deployment: %s", err) + return err + } + + // For a dry run, print the template and exit. + if dry { + format.Info("Template prepared for %s", cfg.Template.Name) + data, err := json.MarshalIndent(cfg.Template, "", "\t") + if err != nil { + return err + } + format.Msg(string(data)) + return nil + } + + if err := cfg.Commit(host); err != nil { format.Error("Failed to commit deployment: %s", err) return err } diff --git a/cmd/helm.go b/cmd/helm.go index d0edde4bb..946b67eb7 100644 --- a/cmd/helm.go +++ b/cmd/helm.go @@ -89,7 +89,7 @@ func commands() []cli.Command { d.Input = os.Stdin } - if err := deploy(d, c.Bool("dry-run")); err != nil { + if err := deploy(d, c.String("host"), c.Bool("dry-run")); err != nil { format.Error("%s (Try running 'helm doctor')", err) os.Exit(1) } @@ -119,6 +119,12 @@ func commands() []cli.Command { Usage: "The default repository", Value: "kubernetes/application-dm-templates", }, + cli.StringFlag{ + Name: "host,u", + Usage: "The URL of the DM server.", + EnvVar: "HELM_HOST", + Value: "https://localhost:8181/FIXME_NOT_RIGHT", + }, }, }, { diff --git a/deploy/deploy.go b/deploy/deploy.go index 038c7eafa..09e6c375f 100644 --- a/deploy/deploy.go +++ b/deploy/deploy.go @@ -13,8 +13,15 @@ import ( "github.com/kubernetes/deployment-manager/registry" ) +// Deployer is capable of deploying an object to a back-end. type Deployer interface { - Commit() error + // Prepare prepares the local side of a deployment. + Prepare() error + + // Commit pushes a deployment and checks that it is completed. + // + // This sends the data to the given host. + Commit(host string) error } // Deployment describes a deployment of a package. @@ -31,10 +38,16 @@ type Deployment struct { Input *os.File // Repository is the location of the templates. Repository string + + // The template, typically generated by the Deployment. + Template *common.Template } -// Commit prepares the Deployment and then commits it to the remote processor. -func (d *Deployment) Commit() error { +// Prepare loads templates and checks for client-side errors. +// +// This will generate the Template based on other information. +func (d *Deployment) Prepare() error { + tpl, err := d.resolveTemplate() if err != nil { return err @@ -45,6 +58,13 @@ func (d *Deployment) Commit() error { tpl.Name = d.Name } + d.Template = tpl + + return nil +} + +// Commit prepares the Deployment and then commits it to the remote processor. +func (d *Deployment) Commit(host string) error { return nil } @@ -129,6 +149,7 @@ func buildTemplateFromType(t *registry.Type, reg string, props map[string]interf }, nil } +// getGitRegistry returns a registry object for a name. func getGitRegistry(reg string) (registry.Registry, error) { s := strings.SplitN(reg, "/", 3) if len(s) < 2 { diff --git a/testdata/guestbook/README.md b/testdata/guestbook/README.md new file mode 100644 index 000000000..b48dd5933 --- /dev/null +++ b/testdata/guestbook/README.md @@ -0,0 +1,127 @@ +T**Testing fork of the guestbook example.** + +# Guestbook Example + +Welcome to the Guestbook example. It shows you how to build and reuse +parameterized templates. + +## Prerequisites + +First, make sure DM is installed in your Kubernetes cluster and that the +Guestbook example is deployed by following the instructions in the top level +[README.md](../../README.md). + +## Understanding the Guestbook example + +Let's take a closer look at the configuration used by the Guestbook example. + +### Replicated services + +The typical design pattern for microservices in Kubernetes is to create a +replication controller and a service with the same selector, so that the service +exposes ports from the pods managed by the replication controller. + +We have created a parameterized template for this kind of replicated service +called [Replicated Service](../../templates/replicatedservice/v1), and we use it +three times in the Guestbook example. + +The template is defined by a +[Python script](../../templates/replicatedservice/v1/replicatedservice.py). It +also has a [schema](../../templates/replicatedservice/v1/replicatedservice.py.schema). +Schemas are optional. If provided, they are used to validate template invocations +that appear in configurations. + +For more information about templates and schemas, see the +[design document](../../docs/design/design.md#templates). + +### The Guestbook application +The Guestbook application consists of 2 microservices: a front end and a Redis +cluster. + +#### The front end + +The front end is a replicated service with 3 replicas: + +``` +- name: frontend + type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/templates/replicatedservice/v1/replicatedservice.py + properties: + service_port: 80 + container_port: 80 + external_service: true + replicas: 3 + image: gcr.io/google_containers/example-guestbook-php-redis:v3 +``` + +(Note that we use the URL for a specific version of the template replicatedservice.py, +not just the template name.) + +#### The Redis cluster + +The Redis cluster consists of two replicated services: a master with a single replica +and the slaves with 2 replicas. It's defined by [this template](../../templates/redis/v1/redis.jinja), +which is a [Jinja](http://jinja.pocoo.org/) file with a [schema](../../templates/redis/v1/redis.jinja.schema). + +``` +{% set REDIS_PORT = 6379 %} +{% set WORKERS = properties['workers'] or 2 %} + +resources: +- name: redis-master + type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/templates/replicatedservice/v1/replicatedservice.py + properties: + # This has to be overwritten since service names are hard coded in the code + service_name: redis-master + service_port: {{ REDIS_PORT }} + target_port: {{ REDIS_PORT }} + container_port: {{ REDIS_PORT }} + replicas: 1 + container_name: master + image: redis + +- name: redis-slave + type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/templates/replicatedservice/v1/replicatedservice.py + properties: + # This has to be overwritten since service names are hard coded in the code + service_name: redis-slave + service_port: {{ REDIS_PORT }} + container_port: {{ REDIS_PORT }} + replicas: {{ WORKERS }} + container_name: worker + image: kubernetes/redis-slave:v2 + # An example of how to specify env variables. + env: + - name: GET_HOSTS_FROM + value: env + - name: REDIS_MASTER_SERVICE_HOST + value: redis-master +``` + +### Displaying types + +You can see both the both primitive types and the templates you've deployed to the +cluster using the `deployed-types` command: + +``` +dm deployed-types + +["Service","ReplicationController","redis.jinja","https://raw.githubusercontent.com/kubernetes/deployment-manager/master/templates/replicatedservice/v1/replicatedservice.py"] +``` + +This output shows 2 primitive types (Service and ReplicationController), and 2 +templates (redis.jinja and one imported from github named replicatedservice.py). + +You can also see where a specific type is being used with the `deployed-instances` command: + +``` +dm deployed-instances Service +[{"name":"frontend-service","type":"Service","deployment":"guestbook4","manifest":"manifest-1446682551242763329","path":"$.resources[0].resources[0]"},{"name":"redis-master","type":"Service","deployment":"guestbook4","manifest":"manifest-1446682551242763329","path":"$.resources[1].resources[0].resources[0]"},{"name":"redis-slave","type":"Service","deployment":"guestbook4","manifest":"manifest-1446682551242763329","path":"$.resources[1].resources[1].resources[0]"}] +``` + +This output describes the deployment and manifest, as well as the JSON paths to +the instances of the type within the layout. + +For more information about deployments, manifests and layouts, see the +[design document](../../docs/design/design.md#api-model). + + diff --git a/testdata/guestbook/guestbook.yaml b/testdata/guestbook/guestbook.yaml new file mode 100644 index 000000000..9a31d2489 --- /dev/null +++ b/testdata/guestbook/guestbook.yaml @@ -0,0 +1,12 @@ +resources: +- name: frontend + type: github.com/kubernetes/application-dm-templates/common/replicatedservice:v1 + properties: + service_port: 80 + container_port: 80 + external_service: true + replicas: 3 + image: gcr.io/google_containers/example-guestbook-php-redis:v3 +- name: redis + type: github.com/kubernetes/application-dm-templates/storage/redis:v1 + properties: null