mirror of https://github.com/helm/helm
291 lines
9.2 KiB
291 lines
9.2 KiB
/*
|
|
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.
|
|
*/
|
|
|
|
package manager
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/kubernetes/deployment-manager/common"
|
|
)
|
|
|
|
// Manager manages a persistent set of Deployments.
|
|
type Manager interface {
|
|
ListDeployments() ([]common.Deployment, error)
|
|
GetDeployment(name string) (*common.Deployment, error)
|
|
CreateDeployment(t *common.Template) (*common.Deployment, error)
|
|
DeleteDeployment(name string, forget bool) (*common.Deployment, error)
|
|
PutDeployment(name string, t *common.Template) (*common.Deployment, error)
|
|
ListManifests(deploymentName string) (map[string]*common.Manifest, error)
|
|
GetManifest(deploymentName string, manifest string) (*common.Manifest, error)
|
|
Expand(t *common.Template) (*common.Manifest, error)
|
|
ListTypes() []string
|
|
ListInstances(typeName string) []*common.TypeInstance
|
|
}
|
|
|
|
type manager struct {
|
|
expander Expander
|
|
deployer Deployer
|
|
repository common.Repository
|
|
}
|
|
|
|
// NewManager returns a new initialized Manager.
|
|
func NewManager(expander Expander, deployer Deployer, repository common.Repository) Manager {
|
|
return &manager{expander, deployer, repository}
|
|
}
|
|
|
|
// ListDeployments returns the list of deployments
|
|
func (m *manager) ListDeployments() ([]common.Deployment, error) {
|
|
l, err := m.repository.ListDeployments()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return l, nil
|
|
}
|
|
|
|
// GetDeployment retrieves the configuration stored for a given deployment
|
|
// as well as the current configuration from the cluster.
|
|
func (m *manager) GetDeployment(name string) (*common.Deployment, error) {
|
|
d, err := m.repository.GetDeployment(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
// ListManifests retrieves the manifests for a given deployment
|
|
// of each of the deployments in the repository and returns the deployments.
|
|
func (m *manager) ListManifests(deploymentName string) (map[string]*common.Manifest, error) {
|
|
l, err := m.repository.ListManifests(deploymentName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return l, nil
|
|
}
|
|
|
|
// GetManifest retrieves the specified manifest for a given deployment
|
|
func (m *manager) GetManifest(deploymentName string, manifestName string) (*common.Manifest, error) {
|
|
d, err := m.repository.GetManifest(deploymentName, manifestName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
// CreateDeployment expands the supplied template, creates the resulting
|
|
// configuration in the cluster, creates a new deployment that tracks it,
|
|
// and stores the deployment in the repository. Returns the deployment.
|
|
func (m *manager) CreateDeployment(t *common.Template) (*common.Deployment, error) {
|
|
log.Printf("Creating deployment: %s", t.Name)
|
|
_, err := m.repository.CreateDeployment(t.Name)
|
|
if err != nil {
|
|
log.Printf("CreateDeployment failed %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
manifest, err := m.createManifest(t)
|
|
if err != nil {
|
|
log.Printf("Manifest creation failed: %v", err)
|
|
m.repository.SetDeploymentStatus(t.Name, common.FailedStatus)
|
|
return nil, err
|
|
}
|
|
|
|
actualConfig, createErr := m.deployer.CreateConfiguration(manifest.ExpandedConfig)
|
|
if createErr != nil {
|
|
// Deployment failed, mark as failed
|
|
log.Printf("CreateConfiguration failed: %v", err)
|
|
m.repository.SetDeploymentStatus(t.Name, common.FailedStatus)
|
|
// If we failed before being able to create some of the resources, then
|
|
// return the failure as such. Otherwise, we're going to add the manifest
|
|
// and hence resource specific errors down below.
|
|
if actualConfig == nil {
|
|
return nil, createErr
|
|
}
|
|
} else {
|
|
m.repository.SetDeploymentStatus(t.Name, common.DeployedStatus)
|
|
}
|
|
|
|
// Update the manifest with the actual state of the reified resources
|
|
manifest.ExpandedConfig = actualConfig
|
|
aErr := m.repository.AddManifest(t.Name, manifest)
|
|
if aErr != nil {
|
|
log.Printf("AddManifest failed %v", aErr)
|
|
m.repository.SetDeploymentStatus(t.Name, common.FailedStatus)
|
|
// If there's an earlier error, return that instead since it contains
|
|
// 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.
|
|
m.addTypeInstances(t.Name, manifest.Name, manifest.Layout)
|
|
return m.repository.GetValidDeployment(t.Name)
|
|
}
|
|
|
|
func (m *manager) createManifest(t *common.Template) (*common.Manifest, error) {
|
|
et, err := m.expander.ExpandTemplate(*t)
|
|
if err != nil {
|
|
log.Printf("Expansion failed %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
return &common.Manifest{
|
|
Name: generateManifestName(),
|
|
Deployment: t.Name,
|
|
InputConfig: t,
|
|
ExpandedConfig: et.Config,
|
|
Layout: et.Layout,
|
|
}, nil
|
|
}
|
|
|
|
func (m *manager) addTypeInstances(deploymentName string, manifestName string, layout *common.Layout) {
|
|
m.repository.ClearTypeInstances(deploymentName)
|
|
|
|
instances := make(map[string][]*common.TypeInstance)
|
|
for i, r := range layout.Resources {
|
|
addTypeInstances(&instances, r, deploymentName, manifestName, fmt.Sprintf("$.resources[%d]", i))
|
|
}
|
|
|
|
m.repository.SetTypeInstances(deploymentName, instances)
|
|
}
|
|
|
|
func addTypeInstances(instances *map[string][]*common.TypeInstance, r *common.LayoutResource, deploymentName string, manifestName string, jsonPath string) {
|
|
// Add this resource.
|
|
inst := &common.TypeInstance{
|
|
Name: r.Name,
|
|
Type: r.Type,
|
|
Deployment: deploymentName,
|
|
Manifest: manifestName,
|
|
Path: jsonPath,
|
|
}
|
|
(*instances)[r.Type] = append((*instances)[r.Type], inst)
|
|
|
|
// Add all sub resources if they exist.
|
|
for i, sr := range r.Resources {
|
|
addTypeInstances(instances, sr, deploymentName, manifestName, fmt.Sprintf("%s.resources[%d]", jsonPath, i))
|
|
}
|
|
}
|
|
|
|
// DeleteDeployment deletes the configuration for the deployment with
|
|
// the supplied identifier from the cluster.repository. If forget is true, then
|
|
// the deployment is removed from the repository. Otherwise, it is marked
|
|
// as deleted and retained.
|
|
func (m *manager) DeleteDeployment(name string, forget bool) (*common.Deployment, error) {
|
|
log.Printf("Deleting deployment: %s", name)
|
|
d, err := m.repository.GetValidDeployment(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If there's a latest manifest, delete the underlying resources.
|
|
latest, err := m.repository.GetLatestManifest(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if latest != nil {
|
|
log.Printf("Deleting resources from the latest manifest")
|
|
if _, err := m.deployer.DeleteConfiguration(latest.ExpandedConfig); err != nil {
|
|
log.Printf("Failed to delete resources from the latest manifest: %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
// Create an empty manifest since resources have been deleted.
|
|
err = m.repository.AddManifest(name, &common.Manifest{Deployment: name, Name: generateManifestName()})
|
|
if err != nil {
|
|
log.Printf("Failed to add empty manifest")
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
d, err = m.repository.DeleteDeployment(name, forget)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Finally remove the type instances for this deployment.
|
|
m.repository.ClearTypeInstances(name)
|
|
|
|
return d, nil
|
|
}
|
|
|
|
// PutDeployment replaces the configuration of the deployment with
|
|
// the supplied identifier in the cluster, and returns the deployment.
|
|
func (m *manager) PutDeployment(name string, t *common.Template) (*common.Deployment, error) {
|
|
_, err := m.repository.GetValidDeployment(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
manifest, err := m.createManifest(t)
|
|
if err != nil {
|
|
log.Printf("Manifest creation failed: %v", err)
|
|
m.repository.SetDeploymentStatus(name, common.FailedStatus)
|
|
return nil, err
|
|
}
|
|
|
|
actualConfig, err := m.deployer.PutConfiguration(manifest.ExpandedConfig)
|
|
if err != nil {
|
|
m.repository.SetDeploymentStatus(name, common.FailedStatus)
|
|
return nil, err
|
|
}
|
|
|
|
manifest.ExpandedConfig = actualConfig
|
|
err = m.repository.AddManifest(t.Name, manifest)
|
|
if err != nil {
|
|
m.repository.SetDeploymentStatus(name, common.FailedStatus)
|
|
return nil, err
|
|
}
|
|
|
|
// Finally update the type instances for this deployment.
|
|
m.addTypeInstances(t.Name, manifest.Name, manifest.Layout)
|
|
|
|
return m.repository.GetValidDeployment(t.Name)
|
|
}
|
|
|
|
func (m *manager) Expand(t *common.Template) (*common.Manifest, error) {
|
|
et, err := m.expander.ExpandTemplate(*t)
|
|
if err != nil {
|
|
log.Printf("Expansion failed %v", err)
|
|
return nil, err
|
|
}
|
|
|
|
return &common.Manifest{
|
|
ExpandedConfig: et.Config,
|
|
Layout: et.Layout,
|
|
}, nil
|
|
}
|
|
|
|
func (m *manager) ListTypes() []string {
|
|
return m.repository.ListTypes()
|
|
}
|
|
|
|
func (m *manager) ListInstances(typeName string) []*common.TypeInstance {
|
|
return m.repository.GetTypeInstances(typeName)
|
|
}
|
|
|
|
func generateManifestName() string {
|
|
return fmt.Sprintf("manifest-%d", time.Now().UTC().UnixNano())
|
|
}
|