Commit dd17c997 authored by jackgr's avatar jackgr

Code changes to support terminology rationalization.

parent e9abaf53
......@@ -36,29 +36,28 @@ import (
)
var (
// TODO(jackgr): Implement reading a template from stdin
//stdin = flag.Bool("stdin", false, "Reads a template from the standard input")
properties = flag.String("properties", "", "Properties to use when deploying a type (e.g., --properties k1=v1,k2=v2)")
type_registry = flag.String("registry", "kubernetes/deployment-manager", "Github based type registry [owner/repo]")
stdin = flag.Bool("stdin", false, "Reads a configuration from the standard input")
properties = flag.String("properties", "", "Properties to use when deploying a template (e.g., --properties k1=v1,k2=v2)")
template_registry = flag.String("registry", "kubernetes/deployment-manager/templates", "Github based template registry (owner/repo[/path])")
service = flag.String("service", "http://localhost:8001/api/v1/proxy/namespaces/default/services/manager-service:manager", "URL for deployment manager")
binary = flag.String("binary", "../expandybird/expansion/expansion.py", "Path to template expansion binary")
)
var commands = []string{
"expand \t\t\t Expands the supplied template(s)",
"deploy \t\t\t Deploys the supplied type or template(s)",
"expand \t\t\t Expands the supplied configuration(s)",
"deploy \t\t\t Deploys the named template or the supplied configuration(s)",
"list \t\t\t Lists the deployments in the cluster",
"get \t\t\t Retrieves the supplied deployment",
"delete \t\t\t Deletes the supplied deployment",
"update \t\t\t Updates a deployment using the supplied template(s)",
"update \t\t\t Updates a deployment using the supplied configuration(s)",
"deployed-types \t\t Lists the types deployed in the cluster",
"deployed-instances \t Lists the instances of the supplied type deployed in the cluster",
"types \t\t\t Lists the types in the current registry",
"describe \t\t Describes the supplied type in the current registry",
"deployed-instances \t Lists the instances of the named type deployed in the cluster",
"templates \t\t Lists the templates in a given template registry",
"describe \t\t Describes the named template in a given template registry",
}
var usage = func() {
message := "Usage: %s [<flags>] <command> (<type-name> | <deployment-name> | (<template> [<import1>...<importN>]))\n"
message := "Usage: %s [<flags>] <command> (<template-name> | <deployment-name> | (<configuration> [<import1>...<importN>]))\n"
fmt.Fprintf(os.Stderr, message, os.Args[0])
fmt.Fprintln(os.Stderr, "Commands:")
for _, command := range commands {
......@@ -73,12 +72,17 @@ var usage = func() {
}
func getGitRegistry() *registry.GithubRegistry {
s := strings.Split(*type_registry, "/")
if len(s) != 2 {
log.Fatalf("invalid type registry: %s", type_registry)
s := strings.Split(*template_registry, "/")
if len(s) < 2 {
log.Fatalf("invalid template registry: %s", *template_registry)
}
return registry.NewGithubRegistry(s[0], s[1])
var path = ""
if len(s) > 2 {
path = strings.Join(s[2:], "/")
}
return registry.NewGithubRegistry(s[0], s[1], path)
}
func main() {
......@@ -89,27 +93,32 @@ func main() {
usage()
}
if *stdin {
fmt.Printf("reading from stdin is not yet implemented")
os.Exit(0)
}
command := args[0]
switch command {
case "types":
case "templates":
git := getGitRegistry()
types, err := git.List()
templates, err := git.List()
if err != nil {
log.Fatalf("Cannot list %v err")
log.Fatalf("Cannot list %v", err)
}
fmt.Printf("Types:\n")
for _, t := range types {
fmt.Printf("Templates:\n")
for _, t := range templates {
fmt.Printf("%s:%s\n", t.Name, t.Version)
downloadURL, err := git.GetURL(t)
if err != nil {
log.Printf("Failed to get download URL for type %s:%s", t.Name, t.Version)
log.Printf("Failed to get download URL for template %s:%s", t.Name, t.Version)
}
fmt.Printf("\tdownload URL: %s\n", downloadURL)
}
case "describe":
fmt.Printf("this feature is not yet implemented")
fmt.Printf("the describe feature is not yet implemented")
case "expand":
backend := expander.NewExpander(*binary)
template := loadTemplate(args)
......@@ -121,7 +130,7 @@ func main() {
fmt.Println(output)
case "deploy":
template := loadTemplate(args)
action := fmt.Sprintf("deploy template named %s", template.Name)
action := fmt.Sprintf("deploy configuration named %s", template.Name)
callService("deployments", "POST", action, marshalTemplate(template))
case "list":
callService("deployments", "GET", "list deployments", nil)
......@@ -149,8 +158,7 @@ func main() {
action := fmt.Sprintf("delete deployment named %s", template.Name)
callService(path, "PUT", action, marshalTemplate(template))
case "deployed-types":
action := fmt.Sprintf("list types in registry %s", *type_registry)
callService("types", "GET", action, nil)
callService("types", "GET", "list deployed types", nil)
case "deployed-instances":
if len(args) < 2 {
fmt.Fprintln(os.Stderr, "No type name supplied")
......@@ -158,7 +166,7 @@ func main() {
}
path := fmt.Sprintf("types/%s/instances", url.QueryEscape(args[1]))
action := fmt.Sprintf("list instances of type %s in registry %s", args[1], *type_registry)
action := fmt.Sprintf("list deployed instances of type %s", args[1])
callService(path, "GET", action, nil)
default:
usage()
......@@ -193,7 +201,7 @@ func loadTemplate(args []string) *expander.Template {
var template *expander.Template
var err error
if len(args) < 2 {
fmt.Fprintln(os.Stderr, "No type name or template file(s) supplied")
fmt.Fprintln(os.Stderr, "No template name or configuration(s) supplied")
usage()
}
......@@ -208,7 +216,7 @@ func loadTemplate(args []string) *expander.Template {
}
if err != nil {
log.Fatalf("cannot create template from supplied arguments: %s\n", err)
log.Fatalf("cannot create configuration from supplied arguments: %s\n", err)
}
return template
......@@ -275,7 +283,7 @@ func buildTemplateFromType(name string, t registry.Type) *expander.Template {
func marshalTemplate(template *expander.Template) io.ReadCloser {
j, err := json.Marshal(template)
if err != nil {
log.Fatalf("cannot deploy template %s: %s\n", template.Name, err)
log.Fatalf("cannot deploy configuration %s: %s\n", template.Name, err)
}
return ioutil.NopCloser(bytes.NewReader(j))
......
resources:
- name: expandybird
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/types/replicatedservice/v1/replicatedservice.py
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/templates/replicatedservice/v1/replicatedservice.py
properties:
service_port: 8081
target_port: 8080
......@@ -11,7 +11,7 @@ resources:
labels:
app: dm
- name: resourcifier
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/types/replicatedservice/v1/replicatedservice.py
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/templates/replicatedservice/v1/replicatedservice.py
properties:
service_port: 8082
target_port: 8080
......@@ -22,7 +22,7 @@ resources:
labels:
app: dm
- name: manager
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/types/replicatedservice/v1/replicatedservice.py
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/templates/replicatedservice/v1/replicatedservice.py
properties:
service_port: 8080
target_port: 8080
......
......@@ -97,7 +97,7 @@ resources:
### Displaying types
You can see both the both primitive types and the templates you deployed to the
You can see both the both primitive types and the templates you've deployed to the
cluster using the `deployed-types` command:
```
......
resources:
- name: frontend
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/types/replicatedservice/v1/replicatedservice.py
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/templates/replicatedservice/v1/replicatedservice.py
properties:
service_port: 80
container_port: 80
......@@ -8,5 +8,5 @@ resources:
replicas: 3
image: gcr.io/google_containers/example-guestbook-php-redis:v3
- name: redis
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/types/redis/v1/redis.jinja
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/templates/redis/v1/redis.jinja
properties: null
......@@ -24,7 +24,7 @@ import (
const invalidFileName = "afilethatdoesnotexist"
var importFileNames = []string{
"../test/replicatedservice.py",
"../../templates/replicatedservice/v1/replicatedservice.py",
}
var outputFileName = "../test/ExpectedOutput.yaml"
......
......@@ -65,7 +65,7 @@ const (
)
var importFileNames = []string{
"../test/replicatedservice.py",
"../../templates/replicatedservice/v1/replicatedservice.py",
}
type ServiceWrapperTestCase struct {
......
......@@ -21,7 +21,6 @@ config:
labels:
app: expandybird
name: expandybird-service
name: expandybird-service
namespace: default
spec:
ports:
......@@ -30,7 +29,6 @@ config:
targetPort: 8080
selector:
app: expandybird
name: expandybird
type: LoadBalancer
type: Service
- name: expandybird-rc
......@@ -41,21 +39,19 @@ config:
labels:
app: expandybird
name: expandybird-rc
name: expandybird-rc
namespace: default
spec:
replicas: 3
selector:
app: expandybird
name: expandybird
template:
metadata:
labels:
app: expandybird
name: expandybird
spec:
containers:
- image: b.gcr.io/dm-k8s-testing/expandybird
- env: []
image: b.gcr.io/dm-k8s-testing/expandybird
name: expandybird
ports:
- containerPort: 8080
......
######################################################################
# Copyright 2015 The Kubernetes Authors All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
######################################################################
"""Defines a ReplicatedService type by creating both a Service and an RC.
This module creates a typical abstraction for running a service in a
Kubernetes cluster, namely a replication controller and a service packaged
together into a single unit.
"""
import yaml
SERVICE_TYPE_COLLECTION = 'Service'
RC_TYPE_COLLECTION = 'ReplicationController'
def GenerateConfig(context):
"""Generates a Replication Controller and a matching Service.
Args:
context: Template context, which can contain the following properties:
container_name - Name to use for container. If omitted, name is
used.
namespace - Namespace to create the resources in. If omitted,
'default' is used.
protocol - Protocol to use for the service
service_port - Port to use for the service
target_port - Target port for the service
container_port - Container port to use
replicas - Number of replicas to create in RC
image - Docker image to use for replicas. Required.
labels - labels to apply.
external_service - If set to true, enable external Load Balancer
Returns:
A Container Manifest as a YAML string.
"""
# YAML config that we're going to create for both RC & Service
config = {'resources': []}
name = context.env['name']
container_name = context.properties.get('container_name', name)
namespace = context.properties.get('namespace', 'default')
# Define things that the Service cares about
service_name = name + '-service'
service_type = SERVICE_TYPE_COLLECTION
# Define things that the Replication Controller (rc) cares about
rc_name = name + '-rc'
rc_type = RC_TYPE_COLLECTION
service = {
'name': service_name,
'type': service_type,
'properties': {
'apiVersion': 'v1',
'kind': 'Service',
'namespace': namespace,
'metadata': {
'name': service_name,
'labels': GenerateLabels(context, service_name),
},
'spec': {
'ports': [GenerateServicePorts(context, container_name)],
'selector': GenerateLabels(context, name)
}
}
}
set_up_external_lb = context.properties.get('external_service', None)
if set_up_external_lb:
service['properties']['spec']['type'] = 'LoadBalancer'
config['resources'].append(service)
rc = {
'name': rc_name,
'type': rc_type,
'properties': {
'apiVersion': 'v1',
'kind': 'ReplicationController',
'namespace': namespace,
'metadata': {
'name': rc_name,
'labels': GenerateLabels(context, rc_name),
},
'spec': {
'replicas': context.properties['replicas'],
'selector': GenerateLabels(context, name),
'template': {
'metadata': {
'labels': GenerateLabels(context, name),
},
'spec': {
'containers': [
{
'name': container_name,
'image': context.properties['image'],
'ports': [
{
'name': container_name,
'containerPort': context.properties['container_port'],
}
]
}
]
}
}
}
}
}
config['resources'].append(rc)
return yaml.dump(config)
# Generates labels either from the context.properties['labels'] or generates
# a default label 'name':name
def GenerateLabels(context, name):
"""Generates labels from context.properties['labels'] or creates default.
We make a deep copy of the context.properties['labels'] section to avoid
linking in the yaml document, which I believe reduces readability of the
expanded template. If no labels are given, generate a default 'name':name.
Args:
context: Template context, which can contain the following properties:
labels - Labels to generate
Returns:
A dict containing labels in a name:value format
"""
tmp_labels = context.properties.get('labels', None)
ret_labels = {'name': name}
if isinstance(tmp_labels, dict):
for key, value in tmp_labels.iteritems():
ret_labels[key] = value
return ret_labels
def GenerateServicePorts(context, name):
"""Generates a ports section for a service.
Args:
context: Template context, which can contain the following properties:
service_port - Port to use for the service
target_port - Target port for the service
protocol - Protocol to use.
Returns:
A dict containing a port definition
"""
service_port = context.properties.get('service_port', None)
target_port = context.properties.get('target_port', None)
protocol = context.properties.get('protocol')
ports = {}
if name:
ports['name'] = name
if service_port:
ports['port'] = service_port
if target_port:
ports['targetPort'] = target_port
if protocol:
ports['protocol'] = protocol
return ports
######################################################################
# Copyright 2015 The Kubernetes Authors All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
######################################################################
root: .
......@@ -24,14 +24,16 @@ import (
type GithubRegistry struct {
owner string
repository string
path string
client *github.Client
}
// NewGithubRegistry creates a Registry that can be used to talk to github.
func NewGithubRegistry(owner string, repository string) *GithubRegistry {
func NewGithubRegistry(owner, repository, path string) *GithubRegistry {
return &GithubRegistry{
owner: owner,
repository: repository,
path: path,
client: github.NewClient(nil),
}
}
......@@ -39,19 +41,21 @@ func NewGithubRegistry(owner string, repository string) *GithubRegistry {
// List the types from the Registry.
func (g *GithubRegistry) List() ([]Type, error) {
// First list all the types at the top level.
types, err := g.getDirs(TypesDir)
types, err := g.getDirs("")
if err != nil {
log.Printf("Failed to list types : %v", err)
log.Printf("Failed to list templates: %v", err)
return nil, err
}
var retTypes []Type
for _, t := range types {
// Then we need to fetch the versions (directories for this type)
versions, err := g.getDirs(TypesDir + "/" + t)
versions, err := g.getDirs(t)
if err != nil {
log.Printf("Failed to fetch versions for type: %s", t)
log.Printf("Failed to fetch versions for template: %s", t)
return nil, err
}
for _, v := range versions {
retTypes = append(retTypes, Type{Name: t, Version: v})
}
......@@ -62,9 +66,10 @@ func (g *GithubRegistry) List() ([]Type, error) {
// GetURL fetches the download URL for a given Type and checks for existence of a schema file.
func (g *GithubRegistry) GetURL(t Type) (string, error) {
_, dc, _, err := g.client.Repositories.GetContents(g.owner, g.repository, TypesDir+"/"+t.Name+"/"+t.Version, nil)
path := g.path + "/" + t.Name + "/" + t.Version
_, dc, _, err := g.client.Repositories.GetContents(g.owner, g.repository, path, nil)
if err != nil {
log.Printf("Failed to list types : %v", err)
log.Printf("Failed to list versions at path: %s: %v", path, err)
return "", err
}
var downloadURL, typeName, schemaName string
......@@ -80,25 +85,32 @@ func (g *GithubRegistry) GetURL(t Type) (string, error) {
}
}
if downloadURL == "" {
return "", fmt.Errorf("Can not find type %s:%s", t.Name, t.Version)
return "", fmt.Errorf("Can not find template %s:%s", t.Name, t.Version)
}
if schemaName == typeName + ".schema" {
if schemaName == typeName+".schema" {
return downloadURL, nil
}
return "", fmt.Errorf("Can not find schema for %s:%s, expected to find %s", t.Name, t.Version, typeName + ".schema")
return "", fmt.Errorf("Can not find schema for %s:%s, expected to find %s", t.Name, t.Version, typeName+".schema")
}
func (g *GithubRegistry) getDirs(dir string) ([]string, error) {
_, dc, _, err := g.client.Repositories.GetContents(g.owner, g.repository, dir, nil)
var path = g.path
if dir != "" {
path = g.path + "/" + dir
}
_, dc, _, err := g.client.Repositories.GetContents(g.owner, g.repository, path, nil)
if err != nil {
log.Printf("Failed to call ListRefs : %v", err)
log.Printf("Failed to get contents at path: %s: %v", path, err)
return nil, err
}
var dirs []string
for _, entry := range dc {
if *entry.Type == "dir" {
dirs = append(dirs, *entry.Name)
}
}
return dirs, nil
}
......@@ -13,24 +13,27 @@ limitations under the License.
package registry
// Registry abstracts a types registry which holds types that can be
// used in a Deployment Manager configurations. A registry root must have
// a 'types' directory which contains all the available types. Each type
// then contains version directories which in turn contains all the files
// necessary for that version of the type.
// For example a type registry holding two types:
// redis v1 (implemented in jinja)
// replicatedservice v2 (implemented in python)
// would have a directory structure like so:
// /types/redis/v1
// Registry abstracts a registry that holds templates, which can be
// used in a Deployment Manager configurations. A registry root must be a
// directory that contains all the available templates, one directory per
// template. Each template directory then contains version directories, each
// of which in turn contains all the files necessary for that version of the
// template.
// For example, a template registry containing two versions of redis
// (implemented in jinja), and one version of replicatedservice (implemented
// in python) would have a directory structure that looks something like this:
// /redis
// /v1
// redis.jinja
// redis.jinja.schema
// /types/replicatedservice/v2
// /v2
// redis.jinja
// redis.jinja.schema
// /replicatedservice
// /v1
// replicatedservice.python
// replicatedservice.python.schema
const TypesDir string = "types"
type Type struct {
Name string
Version string
......@@ -38,8 +41,8 @@ type Type struct {
// Registry abstracts type interactions.
type Registry interface {
// List all the types in the given registry
// List all the templates at the given path
List() ([]Type, error)
// Get the download URL for a given type and version
// Get the download URL for a given template and version
GetURL(t Type) (string, error)
}
......@@ -3,7 +3,7 @@
resources:
- name: redis-master
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/types/replicatedservice/v1/replicatedservice.py
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
......@@ -15,7 +15,7 @@ resources:
image: redis
- name: redis-slave
type: https://raw.githubusercontent.com/kubernetes/deployment-manager/master/types/replicatedservice/v1/replicatedservice.py
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
......
######################################################################
# Copyright 2015 The Kubernetes Authors All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
######################################################################
"""Defines a ReplicatedService type by creating both a Service and an RC.
This module creates a typical abstraction for running a service in a
......
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