Commit 31177c49 authored by Brendan Melville's avatar Brendan Melville

Failures during config creation no longer leave deployments undeleteable

Previously a failure would prematurely exit the creation flow without
attaching the manifest to the deployment. This now always attaches the
manifest, and then updates it in place with the outcome of
resourcification.
parent 22f8a3eb
...@@ -28,30 +28,6 @@ type Schema struct { ...@@ -28,30 +28,6 @@ type Schema struct {
Imports []SchemaImport `json:"imports"` Imports []SchemaImport `json:"imports"`
} }
// Repository manages storage for all Deployment Manager entities, as well as
// the common operations to store, access and manage them.
type Repository interface {
// Deployments.
ListDeployments() ([]Deployment, error)
GetDeployment(name string) (*Deployment, error)
GetValidDeployment(name string) (*Deployment, error)
CreateDeployment(name string) (*Deployment, error)
DeleteDeployment(name string, forget bool) (*Deployment, error)
SetDeploymentStatus(name string, status DeploymentStatus) error
// Manifests.
AddManifest(deploymentName string, manifest *Manifest) error
ListManifests(deploymentName string) (map[string]*Manifest, error)
GetManifest(deploymentName string, manifestName string) (*Manifest, error)
GetLatestManifest(deploymentName string) (*Manifest, error)
// Types.
ListTypes() []string
GetTypeInstances(typeName string) []*TypeInstance
ClearTypeInstances(deploymentName string)
SetTypeInstances(deploymentName string, instances map[string][]*TypeInstance)
}
// Deployment defines a deployment that describes // Deployment defines a deployment that describes
// the creation, modification and/or deletion of a set of resources. // the creation, modification and/or deletion of a set of resources.
type Deployment struct { type Deployment struct {
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"time" "time"
"github.com/kubernetes/deployment-manager/common" "github.com/kubernetes/deployment-manager/common"
"github.com/kubernetes/deployment-manager/manager/repository"
) )
// Manager manages a persistent set of Deployments. // Manager manages a persistent set of Deployments.
...@@ -38,11 +39,11 @@ type Manager interface { ...@@ -38,11 +39,11 @@ type Manager interface {
type manager struct { type manager struct {
expander Expander expander Expander
deployer Deployer deployer Deployer
repository common.Repository repository repository.Repository
} }
// NewManager returns a new initialized Manager. // NewManager returns a new initialized Manager.
func NewManager(expander Expander, deployer Deployer, repository common.Repository) Manager { func NewManager(expander Expander, deployer Deployer, repository repository.Repository) Manager {
return &manager{expander, deployer, repository} return &manager{expander, deployer, repository}
} }
...@@ -105,8 +106,14 @@ func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, erro ...@@ -105,8 +106,14 @@ func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, erro
return nil, err return nil, err
} }
actualConfig, createErr := m.deployer.CreateConfiguration(manifest.ExpandedConfig) if err := m.repository.AddManifest(t.Name, manifest); err != nil {
if createErr != nil { log.Printf("AddManifest failed %v", err)
m.repository.SetDeploymentStatus(t.Name, common.FailedStatus)
return nil, err
}
actualConfig, err := m.deployer.CreateConfiguration(manifest.ExpandedConfig)
if err != nil {
// Deployment failed, mark as failed // Deployment failed, mark as failed
log.Printf("CreateConfiguration failed: %v", err) log.Printf("CreateConfiguration failed: %v", err)
m.repository.SetDeploymentStatus(t.Name, common.FailedStatus) m.repository.SetDeploymentStatus(t.Name, common.FailedStatus)
...@@ -114,7 +121,7 @@ func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, erro ...@@ -114,7 +121,7 @@ func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, erro
// return the failure as such. Otherwise, we're going to add the manifest // return the failure as such. Otherwise, we're going to add the manifest
// and hence resource specific errors down below. // and hence resource specific errors down below.
if actualConfig == nil { if actualConfig == nil {
return nil, createErr return nil, err
} }
} else { } else {
m.repository.SetDeploymentStatus(t.Name, common.DeployedStatus) m.repository.SetDeploymentStatus(t.Name, common.DeployedStatus)
...@@ -122,20 +129,10 @@ func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, erro ...@@ -122,20 +129,10 @@ func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, erro
// Update the manifest with the actual state of the reified resources // Update the manifest with the actual state of the reified resources
manifest.ExpandedConfig = actualConfig manifest.ExpandedConfig = actualConfig
aErr := m.repository.AddManifest(t.Name, manifest) if err := m.repository.SetManifest(t.Name, manifest); err != nil {
if aErr != nil { log.Printf("SetManifest failed %v", err)
log.Printf("AddManifest failed %v", aErr)
m.repository.SetDeploymentStatus(t.Name, common.FailedStatus) m.repository.SetDeploymentStatus(t.Name, common.FailedStatus)
// If there's an earlier error, return that instead since it contains return nil, err
// more applicable error message. Adding manifest failure is more akin
// to a check fail (either deployment doesn't exist, or a manifest with the same
// name already exists).
// TODO(vaikas): Should we combine both errors and return a nicely formatted error for both?
if createErr != nil {
return nil, createErr
} else {
return nil, aErr
}
} }
// Finally update the type instances for this deployment. // Finally update the type instances for this deployment.
......
...@@ -127,6 +127,7 @@ type repositoryStub struct { ...@@ -127,6 +127,7 @@ type repositoryStub struct {
FailListDeployments bool FailListDeployments bool
Created []string Created []string
ManifestAdd map[string]*common.Manifest ManifestAdd map[string]*common.Manifest
ManifestSet map[string]*common.Manifest
Deleted []string Deleted []string
GetValid []string GetValid []string
TypeInstances map[string][]string TypeInstances map[string][]string
...@@ -140,6 +141,7 @@ func (repository *repositoryStub) reset() { ...@@ -140,6 +141,7 @@ func (repository *repositoryStub) reset() {
repository.FailListDeployments = false repository.FailListDeployments = false
repository.Created = make([]string, 0) repository.Created = make([]string, 0)
repository.ManifestAdd = make(map[string]*common.Manifest) repository.ManifestAdd = make(map[string]*common.Manifest)
repository.ManifestSet = make(map[string]*common.Manifest)
repository.Deleted = make([]string, 0) repository.Deleted = make([]string, 0)
repository.GetValid = make([]string, 0) repository.GetValid = make([]string, 0)
repository.TypeInstances = make(map[string][]string) repository.TypeInstances = make(map[string][]string)
...@@ -194,6 +196,11 @@ func (repository *repositoryStub) AddManifest(d string, manifest *common.Manifes ...@@ -194,6 +196,11 @@ func (repository *repositoryStub) AddManifest(d string, manifest *common.Manifes
return nil return nil
} }
func (repository *repositoryStub) SetManifest(d string, manifest *common.Manifest) error {
repository.ManifestSet[d] = manifest
return nil
}
func (repository *repositoryStub) GetLatestManifest(d string) (*common.Manifest, error) { func (repository *repositoryStub) GetLatestManifest(d string) (*common.Manifest, error) {
if d == deploymentName { if d == deploymentName {
return repository.ManifestAdd[d], nil return repository.ManifestAdd[d], nil
...@@ -323,6 +330,11 @@ func TestCreateDeployment(t *testing.T) { ...@@ -323,6 +330,11 @@ func TestCreateDeployment(t *testing.T) {
"to begin with manifest-.", testRepository.ManifestAdd[template.Name].Name) "to begin with manifest-.", testRepository.ManifestAdd[template.Name].Name)
} }
if !strings.HasPrefix(testRepository.ManifestSet[template.Name].Name, "manifest-") {
t.Fatalf("Repository SetManifest was called with %s but expected manifest name"+
"to begin with manifest-.", testRepository.ManifestSet[template.Name].Name)
}
if !reflect.DeepEqual(*testDeployer.Created[0], configuration) || err != nil { if !reflect.DeepEqual(*testDeployer.Created[0], configuration) || err != nil {
t.Fatalf("Deployer CreateConfiguration was called with %s but expected %s.", t.Fatalf("Deployer CreateConfiguration was called with %s but expected %s.",
testDeployer.Created[0], configuration) testDeployer.Created[0], configuration)
...@@ -396,6 +408,11 @@ func TestCreateDeploymentCreationResourceFailure(t *testing.T) { ...@@ -396,6 +408,11 @@ func TestCreateDeploymentCreationResourceFailure(t *testing.T) {
"to begin with manifest-.", testRepository.ManifestAdd[template.Name].Name) "to begin with manifest-.", testRepository.ManifestAdd[template.Name].Name)
} }
if !strings.HasPrefix(testRepository.ManifestSet[template.Name].Name, "manifest-") {
t.Fatalf("Repository SetManifest was called with %s but expected manifest name"+
"to begin with manifest-.", testRepository.ManifestSet[template.Name].Name)
}
if err != nil || !reflect.DeepEqual(d, &deployment) { if err != nil || !reflect.DeepEqual(d, &deployment) {
t.Fatalf("Expected a different set of response values from invoking CreateDeployment.\n"+ t.Fatalf("Expected a different set of response values from invoking CreateDeployment.\n"+
"Received: %v, %v. Expected: %v, %v.", d, err, &deployment, "nil") "Received: %v, %v. Expected: %v, %v.", d, err, &deployment, "nil")
...@@ -425,6 +442,11 @@ func TestDeleteDeploymentForget(t *testing.T) { ...@@ -425,6 +442,11 @@ func TestDeleteDeploymentForget(t *testing.T) {
"to begin with manifest-.", testRepository.ManifestAdd[template.Name].Name) "to begin with manifest-.", testRepository.ManifestAdd[template.Name].Name)
} }
if !strings.HasPrefix(testRepository.ManifestSet[template.Name].Name, "manifest-") {
t.Fatalf("Repository SetManifest was called with %s but expected manifest name"+
"to begin with manifest-.", testRepository.ManifestSet[template.Name].Name)
}
if !reflect.DeepEqual(*testDeployer.Created[0], configuration) || err != nil { if !reflect.DeepEqual(*testDeployer.Created[0], configuration) || err != nil {
t.Fatalf("Deployer CreateConfiguration was called with %s but expected %s.", t.Fatalf("Deployer CreateConfiguration was called with %s but expected %s.",
testDeployer.Created[0], configuration) testDeployer.Created[0], configuration)
......
...@@ -25,6 +25,31 @@ import ( ...@@ -25,6 +25,31 @@ import (
"github.com/kubernetes/deployment-manager/common" "github.com/kubernetes/deployment-manager/common"
) )
// Repository manages storage for all Deployment Manager entities, as well as
// the common operations to store, access and manage them.
type Repository interface {
// Deployments.
ListDeployments() ([]common.Deployment, error)
GetDeployment(name string) (*common.Deployment, error)
GetValidDeployment(name string) (*common.Deployment, error)
CreateDeployment(name string) (*common.Deployment, error)
DeleteDeployment(name string, forget bool) (*common.Deployment, error)
SetDeploymentStatus(name string, status common.DeploymentStatus) error
// Manifests.
AddManifest(deploymentName string, manifest *common.Manifest) error
SetManifest(deploymentName string, manifest *common.Manifest) error
ListManifests(deploymentName string) (map[string]*common.Manifest, error)
GetManifest(deploymentName string, manifestName string) (*common.Manifest, error)
GetLatestManifest(deploymentName string) (*common.Manifest, error)
// Types.
ListTypes() []string
GetTypeInstances(typeName string) []*common.TypeInstance
ClearTypeInstances(deploymentName string)
SetTypeInstances(deploymentName string, instances map[string][]*common.TypeInstance)
}
// deploymentTypeInstanceMap stores type instances mapped by deployment name. // deploymentTypeInstanceMap stores type instances mapped by deployment name.
// This allows for simple updating and deleting of per-deployment instances // This allows for simple updating and deleting of per-deployment instances
// when deployments are created/updated/deleted. // when deployments are created/updated/deleted.
...@@ -39,7 +64,7 @@ type mapBasedRepository struct { ...@@ -39,7 +64,7 @@ type mapBasedRepository struct {
} }
// NewMapBasedRepository returns a new map based repository. // NewMapBasedRepository returns a new map based repository.
func NewMapBasedRepository() common.Repository { func NewMapBasedRepository() Repository {
return &mapBasedRepository{ return &mapBasedRepository{
deployments: make(map[string]common.Deployment, 0), deployments: make(map[string]common.Deployment, 0),
manifests: make(map[string]map[string]*common.Manifest, 0), manifests: make(map[string]map[string]*common.Manifest, 0),
...@@ -129,39 +154,48 @@ func (r *mapBasedRepository) CreateDeployment(name string) (*common.Deployment, ...@@ -129,39 +154,48 @@ func (r *mapBasedRepository) CreateDeployment(name string) (*common.Deployment,
return d, nil return d, nil
} }
// AddManifest adds a manifest to the repository and repoints the latest
// manifest to it for the corresponding deployment.
func (r *mapBasedRepository) AddManifest(deploymentName string, manifest *common.Manifest) error { func (r *mapBasedRepository) AddManifest(deploymentName string, manifest *common.Manifest) error {
err := func() error { r.Lock()
r.Lock() defer r.Unlock()
defer r.Unlock()
d, err := r.GetValidDeployment(deploymentName) l, err := r.listManifestsForDeployment(deploymentName)
if err != nil { if err != nil {
return err return err
} }
l, err := r.listManifestsForDeployment(deploymentName) // Make sure the manifest doesn't already exist, and if not, add the manifest to
if err != nil { // map of manifests this deployment has
return err if _, ok := l[manifest.Name]; ok {
} return fmt.Errorf("Manifest %s already exists in deployment %s", manifest.Name, deploymentName)
}
// Make sure the manifest doesn't already exist, and if not, add the manifest to d, err := r.GetValidDeployment(deploymentName)
// map of manifests this deployment has if err != nil {
if _, ok := l[manifest.Name]; ok { return err
return fmt.Errorf("Manifest %s already exists in deployment %s", manifest.Name, deploymentName) }
}
l[manifest.Name] = manifest l[manifest.Name] = manifest
d.LatestManifest = manifest.Name d.LatestManifest = manifest.Name
r.deployments[deploymentName] = *d r.deployments[deploymentName] = *d
return nil log.Printf("Added manifest %s to deployment: %s", manifest.Name, deploymentName)
}() return nil
}
// SetManifest sets an existing manifest in the repository to provided
// manifest.
func (r *mapBasedRepository) SetManifest(deploymentName string, manifest *common.Manifest) error {
r.Lock()
defer r.Unlock()
l, err := r.listManifestsForDeployment(deploymentName)
if err != nil { if err != nil {
return err return err
} }
log.Printf("Added manifest %s to deployment: %s", manifest.Name, deploymentName) l[manifest.Name] = manifest
return nil return nil
} }
......
...@@ -82,11 +82,15 @@ func testCreateDeploymentWithManifests(t *testing.T, count int) { ...@@ -82,11 +82,15 @@ func testCreateDeploymentWithManifests(t *testing.T, count int) {
if err != nil { if err != nil {
t.Fatalf("AddManifest failed: %v", err) t.Fatalf("AddManifest failed: %v", err)
} }
_, err = r.GetDeployment(deploymentName) d, err = r.GetDeployment(deploymentName)
if err != nil { if err != nil {
t.Fatalf("GetDeployment failed: %v", err) t.Fatalf("GetDeployment failed: %v", err)
} }
if d.LatestManifest != manifestName {
t.Fatalf("AddManifest did not update LatestManifest: %#v", d)
}
mListNew, err := r.ListManifests(deploymentName) mListNew, err := r.ListManifests(deploymentName)
if err != nil { if err != nil {
t.Fatalf("ListManifests failed: %v", err) t.Fatalf("ListManifests failed: %v", err)
......
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