Commit 376d3399 authored by vaikas-google's avatar vaikas-google

Merge pull request #142 from bmelville/refs

Resourcifier now processes resources in dependency order based on refs.
parents d3d57fda 22f8a3eb
...@@ -7,6 +7,9 @@ resources: ...@@ -7,6 +7,9 @@ resources:
external_service: true external_service: true
replicas: 3 replicas: 3
image: gcr.io/google_containers/example-guestbook-php-redis:v3 image: gcr.io/google_containers/example-guestbook-php-redis:v3
env:
- name: redis-master
value: $(ref.redis-master.name)
- name: redis - name: redis
type: github.com/kubernetes/application-dm-templates/storage/redis:v1 type: github.com/kubernetes/application-dm-templates/storage/redis:v1
properties: null properties: null
...@@ -206,6 +206,11 @@ func (m *manager) DeleteDeployment(name string, forget bool) (*common.Deployment ...@@ -206,6 +206,11 @@ func (m *manager) DeleteDeployment(name string, forget bool) (*common.Deployment
if latest != nil { if latest != nil {
log.Printf("Deleting resources from the latest manifest") log.Printf("Deleting resources from the latest manifest")
// Clear previous state.
for _, r := range latest.ExpandedConfig.Resources {
r.State = nil
}
if _, err := m.deployer.DeleteConfiguration(latest.ExpandedConfig); err != nil { if _, err := m.deployer.DeleteConfiguration(latest.ExpandedConfig); err != nil {
log.Printf("Failed to delete resources from the latest manifest: %v", err) log.Printf("Failed to delete resources from the latest manifest: %v", err)
return nil, err return nil, err
......
...@@ -18,10 +18,11 @@ import ( ...@@ -18,10 +18,11 @@ import (
"fmt" "fmt"
"log" "log"
"os/exec" "os/exec"
"regexp"
"strings" "strings"
"github.com/kubernetes/deployment-manager/common"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"github.com/kubernetes/deployment-manager/common"
) )
// TODO(jackgr): Define an interface and a struct type for Configurator and move initialization to the caller. // TODO(jackgr): Define an interface and a struct type for Configurator and move initialization to the caller.
...@@ -73,6 +74,11 @@ func (e *Error) appendError(err error) error { ...@@ -73,6 +74,11 @@ func (e *Error) appendError(err error) error {
return err return err
} }
// resource name -> set of dependencies.
type DependencyMap map[string]map[string]bool
var refRe = regexp.MustCompile("\\$\\(ref\\.([^\\.]+)\\.([^\\)]+)\\)")
// Configure passes each resource in the configuration to kubectl and performs the appropriate // Configure passes each resource in the configuration to kubectl and performs the appropriate
// action on it (create/delete/replace) and updates the State of the resource with the resulting // action on it (create/delete/replace) and updates the State of the resource with the resulting
// status. In case of errors with a resource, Resource.State.Errors is set. // status. In case of errors with a resource, Resource.State.Errors is set.
...@@ -80,7 +86,46 @@ func (e *Error) appendError(err error) error { ...@@ -80,7 +86,46 @@ func (e *Error) appendError(err error) error {
func (a *Configurator) Configure(c *common.Configuration, o operation) (string, error) { func (a *Configurator) Configure(c *common.Configuration, o operation) (string, error) {
errors := &Error{} errors := &Error{}
var output []string var output []string
for i, resource := range c.Resources {
deps, err := getDependencies(c, o)
if err != nil {
e := fmt.Errorf("Error generating dependencies: %s", err.Error())
return "", e
}
for {
resources := getUnprocessedResources(c)
// No more resources to process.
if len(resources) == 0 {
break
}
for _, r := range resources {
// Resource still has dependencies.
if len(deps[r.Name]) != 0 {
continue
}
out, err := a.configureResource(r, o)
if err != nil {
log.Println(errors.appendError(err))
abortDependants(c, deps, r.Name)
// Resource states have changed, need to recalculate unprocessed
// resources.
break
}
output = append(output, out)
removeDependencies(deps, r.Name)
}
}
return strings.Join(output, "\n"), nil
}
func (a *Configurator) configureResource(resource *common.Resource, o operation) (string, error) {
args := []string{o.String()} args := []string{o.String()}
if o == GetOperation { if o == GetOperation {
args = append(args, "-o", "yaml") args = append(args, "-o", "yaml")
...@@ -98,12 +143,8 @@ func (a *Configurator) Configure(c *common.Configuration, o operation) (string, ...@@ -98,12 +143,8 @@ func (a *Configurator) Configure(c *common.Configuration, o operation) (string,
y, err = yaml.Marshal(resource.Properties) y, err = yaml.Marshal(resource.Properties)
if err != nil { if err != nil {
e := fmt.Errorf("yaml marshal failed for resource: %v: %v", resource.Name, err) e := fmt.Errorf("yaml marshal failed for resource: %v: %v", resource.Name, err)
log.Println(errors.appendError(e)) resource.State = failState(e)
c.Resources[i].State = &common.ResourceState{ return "", e
Status: common.Aborted,
Errors: []string{e.Error()},
}
continue
} }
} }
...@@ -122,34 +163,99 @@ func (a *Configurator) Configure(c *common.Configuration, o operation) (string, ...@@ -122,34 +163,99 @@ func (a *Configurator) Configure(c *common.Configuration, o operation) (string,
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
e := fmt.Errorf("cannot start kubetcl for resource: %v: %v", resource.Name, err) e := fmt.Errorf("cannot start kubetcl for resource: %v: %v", resource.Name, err)
c.Resources[i].State = &common.ResourceState{ resource.State = failState(e)
Status: common.Failed, return "", e
Errors: []string{e.Error()},
}
log.Println(errors.appendError(e))
continue
} }
if err := cmd.Wait(); err != nil { if err := cmd.Wait(); err != nil {
// Treat delete special. If a delete is issued and a resource is not found, treat it as // Treat delete special. If a delete is issued and a resource is not found, treat it as
// success. // success.
if (o == DeleteOperation && strings.HasSuffix(strings.TrimSpace(combined.String()), "not found")) { if o == DeleteOperation && strings.HasSuffix(strings.TrimSpace(combined.String()), "not found") {
log.Println(resource.Name + " not found, treating as success for delete") log.Println(resource.Name + " not found, treating as success for delete")
} else { } else {
e := fmt.Errorf("kubetcl failed for resource: %v: %v: %v", resource.Name, err, combined.String()) e := fmt.Errorf("kubetcl failed for resource: %v: %v: %v", resource.Name, err, combined.String())
c.Resources[i].State = &common.ResourceState{ resource.State = failState(e)
return "", e
}
}
log.Printf("kubectl succeeded for resource: %v: SysTime: %v UserTime: %v\n%v",
resource.Name, cmd.ProcessState.SystemTime(), cmd.ProcessState.UserTime(), combined.String())
resource.State = &common.ResourceState{Status: common.Created}
return combined.String(), nil
}
func failState(e error) *common.ResourceState {
return &common.ResourceState{
Status: common.Failed, Status: common.Failed,
Errors: []string{e.Error()}, Errors: []string{e.Error()},
} }
log.Println(errors.appendError(e)) }
continue
func getUnprocessedResources(c *common.Configuration) []*common.Resource {
var resources []*common.Resource
for _, r := range c.Resources {
if r.State == nil {
resources = append(resources, r)
} }
} }
output = append(output, combined.String()) return resources
c.Resources[i].State = &common.ResourceState{Status: common.Created} }
log.Printf("kubectl succeeded for resource: %v: SysTime: %v UserTime: %v\n%v",
resource.Name, cmd.ProcessState.SystemTime(), cmd.ProcessState.UserTime(), combined.String()) // getDependencies iterates over resources and returns a map of resource name to
// the set of dependencies that resource has.
//
// Dependencies are reversed for delete operation.
func getDependencies(c *common.Configuration, o operation) (DependencyMap, error) {
deps := DependencyMap{}
// Prepopulate map. This will be used later to validate referenced resources
// actually exist.
for _, r := range c.Resources {
deps[r.Name] = make(map[string]bool)
}
for _, r := range c.Resources {
props, err := yaml.Marshal(r.Properties)
if err != nil {
return nil, fmt.Errorf("Failed to deserialize resource properties for resource %s: %v", r.Name, r.Properties)
}
refs := refRe.FindAllStringSubmatch(string(props), -1)
for _, ref := range refs {
// Validate referenced resource exists in config.
if _, ok := deps[ref[1]]; !ok {
return nil, fmt.Errorf("Invalid resource name in reference: %s", ref[1])
}
// Delete dependencies should be reverse of create.
if o == DeleteOperation {
deps[ref[1]][r.Name] = true
} else {
deps[r.Name][ref[1]] = true
}
}
}
return deps, nil
}
// updateDependants removes the dependency dep from the set of dependencies for
// all resource.
func removeDependencies(deps DependencyMap, dep string) {
for _, d := range deps {
delete(d, dep)
}
}
// abortDependants changes the state of all of the dependants of a resource to
// Aborted.
func abortDependants(c *common.Configuration, deps DependencyMap, dep string) {
for _, r := range c.Resources {
if _, ok := deps[r.Name][dep]; ok {
r.State = &common.ResourceState{Status: common.Aborted}
}
} }
return strings.Join(output, "\n"), nil
} }
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