mirror of https://github.com/helm/helm
parent
e1a76a409c
commit
fdc16739f6
@ -0,0 +1,167 @@
|
||||
######################################################################
|
||||
# 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.
|
||||
######################################################################
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: dm-namespace
|
||||
name: dm
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: expandybird-service
|
||||
name: expandybird-service
|
||||
namespace: dm
|
||||
spec:
|
||||
ports:
|
||||
- name: expandybird
|
||||
port: 8081
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: dm
|
||||
name: expandybird
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: expandybird-rc
|
||||
name: expandybird-rc
|
||||
namespace: dm
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
app: dm
|
||||
name: expandybird
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: expandybird
|
||||
spec:
|
||||
containers:
|
||||
- env: []
|
||||
image: gcr.io/dm-k8s-test-jackgr/expandybird:latest
|
||||
name: expandybird
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: expandybird
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: resourcifier-service
|
||||
name: resourcifier-service
|
||||
namespace: dm
|
||||
spec:
|
||||
ports:
|
||||
- name: resourcifier
|
||||
port: 8082
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: dm
|
||||
name: resourcifier
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: resourcifier-rc
|
||||
name: resourcifier-rc
|
||||
namespace: dm
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
app: dm
|
||||
name: resourcifier
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: resourcifier
|
||||
spec:
|
||||
containers:
|
||||
- env: []
|
||||
image: gcr.io/dm-k8s-test-jackgr/resourcifier:latest
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 1
|
||||
name: resourcifier
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: resourcifier
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: manager-service
|
||||
name: manager-service
|
||||
namespace: dm
|
||||
spec:
|
||||
ports:
|
||||
- name: manager
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
selector:
|
||||
app: dm
|
||||
name: manager
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: manager-rc
|
||||
name: manager-rc
|
||||
namespace: dm
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
app: dm
|
||||
name: manager
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: dm
|
||||
name: manager
|
||||
spec:
|
||||
containers:
|
||||
- env: []
|
||||
image: gcr.io/dm-k8s-test-jackgr/manager:latest
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 1
|
||||
name: manager
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: manager
|
@ -0,0 +1,488 @@
|
||||
/*
|
||||
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 persistent implements a persistent deployment repository.
|
||||
//
|
||||
// This package is currently implemented using MondoDB, but there is no
|
||||
// guarantee that it will continue to be implemented using MondoDB in the
|
||||
// future.
|
||||
package persistent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/kubernetes/deployment-manager/common"
|
||||
"github.com/kubernetes/deployment-manager/manager/repository"
|
||||
|
||||
"gopkg.in/mgo.v2"
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
type pDeployment struct {
|
||||
ID string `bson:"_id"`
|
||||
common.Deployment
|
||||
}
|
||||
|
||||
type pManifest struct {
|
||||
ID string `bson:"_id"`
|
||||
common.Manifest
|
||||
}
|
||||
|
||||
type pInstance struct {
|
||||
ID string `bson:"_id"`
|
||||
common.TypeInstance
|
||||
}
|
||||
|
||||
type pRepository struct {
|
||||
Session *mgo.Session // mongodb session
|
||||
Deployments *mgo.Collection // deployments collection
|
||||
Manifests *mgo.Collection // manifests collection
|
||||
Instances *mgo.Collection // instances collection
|
||||
}
|
||||
|
||||
// Constants used to configure the MongoDB database.
|
||||
const (
|
||||
DatabaseName = "deployment_manager"
|
||||
DeploymentsCollectionName = "deployments_collection"
|
||||
ManifestsCollectionName = "manifests_collection"
|
||||
InstancesCollectionName = "instances_collection"
|
||||
)
|
||||
|
||||
// NewRepository returns a new persistent repository. Its lifetime is decopuled
|
||||
// from the lifetime of the current process. When the process dies, its contents
|
||||
// will not be affected.
|
||||
//
|
||||
// The server argument provides connection information for the repository server.
|
||||
// It is parsed as a URL, and the username, password, host and port, if provided,
|
||||
// are used to create the connection string.
|
||||
func NewRepository(server string) (repository.Repository, error) {
|
||||
travis := os.Getenv("TRAVIS")
|
||||
if travis == "true" {
|
||||
err := fmt.Errorf("cannot use MongoDB in Travis CI due to gopkg.in/mgo.v2 issue #218")
|
||||
log.Println(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := url.Parse(server)
|
||||
if err != nil {
|
||||
err2 := fmt.Errorf("cannot parse url '%s': %s\n", server, err)
|
||||
log.Println(err2.Error())
|
||||
return nil, err2
|
||||
}
|
||||
|
||||
u2 := &url.URL{Scheme: "mongodb", User: u.User, Host: u.Host}
|
||||
server = u2.String()
|
||||
|
||||
session, err := mgo.Dial(server)
|
||||
if err != nil {
|
||||
err2 := fmt.Errorf("cannot connect to MongoDB at %s: %s\n", server, err)
|
||||
log.Println(err2.Error())
|
||||
return nil, err2
|
||||
}
|
||||
|
||||
session.SetMode(mgo.Strong, false)
|
||||
session.SetSafe(&mgo.Safe{WMode: "majority"})
|
||||
database := session.DB(DatabaseName)
|
||||
deployments, err := createCollection(database, DeploymentsCollectionName, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manifests, err := createCollection(database, ManifestsCollectionName,
|
||||
[][]string{{"manifest.deployment"}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instances, err := createCollection(database, InstancesCollectionName,
|
||||
[][]string{{"typeinstance.type"}, {"typeinstance.deployment"}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pr := &pRepository{
|
||||
Session: session,
|
||||
Deployments: deployments,
|
||||
Manifests: manifests,
|
||||
Instances: instances,
|
||||
}
|
||||
|
||||
return pr, nil
|
||||
}
|
||||
|
||||
func createCollection(db *mgo.Database, cName string, keys [][]string) (*mgo.Collection, error) {
|
||||
c := db.C(cName)
|
||||
for _, key := range keys {
|
||||
if err := createIndex(c, key...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func createIndex(c *mgo.Collection, key ...string) error {
|
||||
if err := c.EnsureIndexKey(key...); err != nil {
|
||||
err2 := fmt.Errorf("cannot create index %v for collection %s: %s\n", key, c.Name, err)
|
||||
log.Println(err2.Error())
|
||||
return err2
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset returns the repository to its initial state.
|
||||
func (r *pRepository) Reset() error {
|
||||
database := r.Session.DB(DatabaseName)
|
||||
if err := database.DropDatabase(); err != nil {
|
||||
return fmt.Errorf("cannot drop database %s", database.Name)
|
||||
}
|
||||
|
||||
r.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close cleans up any resources used by the repository.
|
||||
func (r *pRepository) Close() {
|
||||
r.Session.Close()
|
||||
}
|
||||
|
||||
// ListDeployments returns of all of the deployments in the repository.
|
||||
func (r *pRepository) ListDeployments() ([]common.Deployment, error) {
|
||||
var result []pDeployment
|
||||
if err := r.Deployments.Find(nil).All(&result); err != nil {
|
||||
return nil, fmt.Errorf("cannot list deployments: %s", err)
|
||||
}
|
||||
|
||||
deployments := []common.Deployment{}
|
||||
for _, pd := range result {
|
||||
deployments = append(deployments, pd.Deployment)
|
||||
}
|
||||
|
||||
return deployments, nil
|
||||
}
|
||||
|
||||
// GetDeployment returns the deployment with the supplied name.
|
||||
// If the deployment is not found, it returns an error.
|
||||
func (r *pRepository) GetDeployment(name string) (*common.Deployment, error) {
|
||||
result := pDeployment{}
|
||||
if err := r.Deployments.FindId(name).One(&result); err != nil {
|
||||
return nil, fmt.Errorf("cannot get deployment %s: %s", name, err)
|
||||
}
|
||||
|
||||
return &result.Deployment, nil
|
||||
}
|
||||
|
||||
// GetValidDeployment returns the deployment with the supplied name.
|
||||
// If the deployment is not found or marked as deleted, it returns an error.
|
||||
func (r *pRepository) GetValidDeployment(name string) (*common.Deployment, error) {
|
||||
d, err := r.GetDeployment(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if d.State.Status == common.DeletedStatus {
|
||||
return nil, fmt.Errorf("deployment %s is deleted", name)
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDeployment creates a new deployment and stores it in the repository.
|
||||
func (r *pRepository) CreateDeployment(name string) (*common.Deployment, error) {
|
||||
exists, _ := r.GetValidDeployment(name)
|
||||
if exists != nil {
|
||||
return nil, fmt.Errorf("deployment %s already exists", name)
|
||||
}
|
||||
|
||||
d := common.NewDeployment(name)
|
||||
if err := r.insertDeployment(d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("created deployment: %v", d)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// SetDeploymentStatus sets the DeploymentStatus of the deployment and updates ModifiedAt
|
||||
func (r *pRepository) SetDeploymentState(name string, state *common.DeploymentState) error {
|
||||
d, err := r.GetValidDeployment(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.State = state
|
||||
return r.updateDeployment(d)
|
||||
}
|
||||
|
||||
func (r *pRepository) AddManifest(manifest *common.Manifest) error {
|
||||
deploymentName := manifest.Deployment
|
||||
d, err := r.GetValidDeployment(deploymentName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
count, err := r.Manifests.FindId(manifest.Name).Count()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot search for manifest %s: %s", manifest.Name, err)
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
return fmt.Errorf("manifest %s already exists", manifest.Name)
|
||||
}
|
||||
|
||||
if err := r.insertManifest(manifest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.LatestManifest = manifest.Name
|
||||
if err := r.updateDeployment(d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Added manifest %s to deployment: %s", manifest.Name, deploymentName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteDeployment deletes the deployment with the supplied name.
|
||||
// If forget is true, then the deployment is removed from the repository.
|
||||
// Otherwise, it is marked as deleted and retained.
|
||||
func (r *pRepository) DeleteDeployment(name string, forget bool) (*common.Deployment, error) {
|
||||
d, err := r.GetValidDeployment(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !forget {
|
||||
d.DeletedAt = time.Now()
|
||||
d.State = &common.DeploymentState{Status: common.DeletedStatus}
|
||||
if err := r.updateDeployment(d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
d.LatestManifest = ""
|
||||
if err := r.removeManifestsForDeployment(d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.removeDeployment(d); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("deleted deployment: %v", d)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (r *pRepository) insertDeployment(d *common.Deployment) error {
|
||||
if d != nil && d.Name != "" {
|
||||
wrapper := pDeployment{ID: d.Name, Deployment: *d}
|
||||
if err := r.Deployments.Insert(&wrapper); err != nil {
|
||||
return fmt.Errorf("cannot insert deployment %v: %s", wrapper, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *pRepository) removeDeployment(d *common.Deployment) error {
|
||||
if d != nil && d.Name != "" {
|
||||
if err := r.Deployments.RemoveId(d.Name); err != nil {
|
||||
return fmt.Errorf("cannot remove deployment %s: %s", d.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *pRepository) updateDeployment(d *common.Deployment) error {
|
||||
if d != nil && d.Name != "" {
|
||||
if d.State.Status != common.DeletedStatus {
|
||||
d.ModifiedAt = time.Now()
|
||||
}
|
||||
|
||||
wrapper := pDeployment{ID: d.Name, Deployment: *d}
|
||||
if err := r.Deployments.UpdateId(d.Name, &wrapper); err != nil {
|
||||
return fmt.Errorf("cannot update deployment %v: %s", wrapper, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *pRepository) ListManifests(deploymentName string) (map[string]*common.Manifest, error) {
|
||||
_, err := r.GetValidDeployment(deploymentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.listManifestsForDeployment(deploymentName)
|
||||
}
|
||||
|
||||
func (r *pRepository) GetManifest(deploymentName string, manifestName string) (*common.Manifest, error) {
|
||||
_, err := r.GetValidDeployment(deploymentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.getManifestForDeployment(deploymentName, manifestName)
|
||||
}
|
||||
|
||||
// GetLatestManifest returns the latest manifest for a given deployment,
|
||||
// which by definition is the manifest with the largest time stamp.
|
||||
func (r *pRepository) GetLatestManifest(deploymentName string) (*common.Manifest, error) {
|
||||
d, err := r.GetValidDeployment(deploymentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if d.LatestManifest == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return r.getManifestForDeployment(deploymentName, d.LatestManifest)
|
||||
}
|
||||
|
||||
// SetManifest sets an existing manifest in the repository to provided manifest.
|
||||
func (r *pRepository) SetManifest(manifest *common.Manifest) error {
|
||||
_, err := r.GetManifest(manifest.Deployment, manifest.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.updateManifest(manifest)
|
||||
}
|
||||
|
||||
func (r *pRepository) updateManifest(m *common.Manifest) error {
|
||||
if m != nil && m.Name != "" {
|
||||
wrapper := pManifest{ID: m.Name, Manifest: *m}
|
||||
if err := r.Manifests.UpdateId(m.Name, &wrapper); err != nil {
|
||||
return fmt.Errorf("cannot update manifest %v: %s", wrapper, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *pRepository) listManifestsForDeployment(deploymentName string) (map[string]*common.Manifest, error) {
|
||||
query := bson.M{"manifest.deployment": deploymentName}
|
||||
var result []pManifest
|
||||
if err := r.Manifests.Find(query).All(&result); err != nil {
|
||||
return nil, fmt.Errorf("cannot list manifests for deployment %s: %s", deploymentName, err)
|
||||
}
|
||||
|
||||
l := make(map[string]*common.Manifest, 0)
|
||||
for _, pm := range result {
|
||||
l[pm.Name] = &pm.Manifest
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (r *pRepository) getManifestForDeployment(deploymentName string, manifestName string) (*common.Manifest, error) {
|
||||
result := pManifest{}
|
||||
if err := r.Manifests.FindId(manifestName).One(&result); err != nil {
|
||||
return nil, fmt.Errorf("cannot get manifest %s: %s", manifestName, err)
|
||||
}
|
||||
|
||||
if result.Deployment != deploymentName {
|
||||
return nil, fmt.Errorf("manifest %s not found in deployment %s", manifestName, deploymentName)
|
||||
}
|
||||
|
||||
return &result.Manifest, nil
|
||||
}
|
||||
|
||||
func (r *pRepository) insertManifest(m *common.Manifest) error {
|
||||
if m != nil && m.Name != "" {
|
||||
wrapper := pManifest{ID: m.Name, Manifest: *m}
|
||||
if err := r.Manifests.Insert(&wrapper); err != nil {
|
||||
return fmt.Errorf("cannot insert manifest %v: %s", wrapper, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *pRepository) removeManifestsForDeployment(d *common.Deployment) error {
|
||||
if d != nil && d.Name != "" {
|
||||
query := bson.M{"manifest.deployment": d.Name}
|
||||
if _, err := r.Manifests.RemoveAll(query); err != nil {
|
||||
return fmt.Errorf("cannot remove all manifests for deployment %s: %s", d.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListTypes returns all types known from existing instances.
|
||||
func (r *pRepository) ListTypes() ([]string, error) {
|
||||
var result []string
|
||||
if err := r.Instances.Find(nil).Distinct("typeinstance.type", &result); err != nil {
|
||||
return nil, fmt.Errorf("cannot list type instances: %s", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetTypeInstances returns all instances of a given type. If typeName is empty
|
||||
// or equal to "all", returns all instances of all types.
|
||||
func (r *pRepository) GetTypeInstances(typeName string) ([]*common.TypeInstance, error) {
|
||||
query := bson.M{"typeinstance.type": typeName}
|
||||
if typeName == "" || typeName == "all" {
|
||||
query = nil
|
||||
}
|
||||
|
||||
var result []pInstance
|
||||
if err := r.Instances.Find(query).All(&result); err != nil {
|
||||
return nil, fmt.Errorf("cannot get instances of type %s: %s", typeName, err)
|
||||
}
|
||||
|
||||
instances := []*common.TypeInstance{}
|
||||
for _, pi := range result {
|
||||
instances = append(instances, &pi.TypeInstance)
|
||||
}
|
||||
|
||||
return instances, nil
|
||||
}
|
||||
|
||||
// ClearTypeInstancesForDeployment deletes all type instances associated with the given
|
||||
// deployment from the repository.
|
||||
func (r *pRepository) ClearTypeInstancesForDeployment(deploymentName string) error {
|
||||
if deploymentName != "" {
|
||||
query := bson.M{"typeinstance.deployment": deploymentName}
|
||||
if _, err := r.Instances.RemoveAll(query); err != nil {
|
||||
return fmt.Errorf("cannot clear type instances for deployment %s: %s", deploymentName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTypeInstances adds the supplied type instances to the repository.
|
||||
func (r *pRepository) AddTypeInstances(instances map[string][]*common.TypeInstance) error {
|
||||
for _, is := range instances {
|
||||
for _, i := range is {
|
||||
key := fmt.Sprintf("%s.%s.%s", i.Deployment, i.Type, i.Name)
|
||||
wrapper := pInstance{ID: key, TypeInstance: *i}
|
||||
if err := r.Instances.Insert(&wrapper); err != nil {
|
||||
return fmt.Errorf("cannot insert type instance %v: %s", wrapper, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
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 persistent
|
||||
|
||||
import (
|
||||
"github.com/kubernetes/deployment-manager/manager/repository"
|
||||
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var tryRepository = true
|
||||
var repositoryLock sync.RWMutex
|
||||
|
||||
func createRepository() repository.Repository {
|
||||
repositoryLock.Lock()
|
||||
defer repositoryLock.Unlock()
|
||||
|
||||
if tryRepository {
|
||||
r, err := NewRepository("mongodb://localhost")
|
||||
if err == nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
tryRepository = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetRepository(t *testing.T, r repository.Repository) {
|
||||
if r != nil {
|
||||
if err := r.(*pRepository).Reset(); err != nil {
|
||||
t.Fatalf("cannot reset repository: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryListEmpty(t *testing.T) {
|
||||
if r := createRepository(); r != nil {
|
||||
defer resetRepository(t, r)
|
||||
repository.TestRepositoryListEmpty(t, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryGetFailsWithNonExistentDeployment(t *testing.T) {
|
||||
if r := createRepository(); r != nil {
|
||||
defer resetRepository(t, r)
|
||||
repository.TestRepositoryGetFailsWithNonExistentDeployment(t, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryCreateDeploymentWorks(t *testing.T) {
|
||||
if r := createRepository(); r != nil {
|
||||
defer resetRepository(t, r)
|
||||
repository.TestRepositoryCreateDeploymentWorks(t, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryMultipleManifestsWorks(t *testing.T) {
|
||||
if r := createRepository(); r != nil {
|
||||
defer resetRepository(t, r)
|
||||
repository.TestRepositoryMultipleManifestsWorks(t, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryDeleteFailsWithNonExistentDeployment(t *testing.T) {
|
||||
if r := createRepository(); r != nil {
|
||||
defer resetRepository(t, r)
|
||||
repository.TestRepositoryDeleteFailsWithNonExistentDeployment(t, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryDeleteWorksWithNoLatestManifest(t *testing.T) {
|
||||
if r := createRepository(); r != nil {
|
||||
defer resetRepository(t, r)
|
||||
repository.TestRepositoryDeleteWorksWithNoLatestManifest(t, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryDeleteDeploymentWorksNoForget(t *testing.T) {
|
||||
if r := createRepository(); r != nil {
|
||||
defer resetRepository(t, r)
|
||||
repository.TestRepositoryDeleteDeploymentWorksNoForget(t, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryDeleteDeploymentWorksForget(t *testing.T) {
|
||||
if r := createRepository(); r != nil {
|
||||
defer resetRepository(t, r)
|
||||
repository.TestRepositoryDeleteDeploymentWorksForget(t, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepositoryTypeInstances(t *testing.T) {
|
||||
if r := createRepository(); r != nil {
|
||||
defer resetRepository(t, r)
|
||||
repository.TestRepositoryTypeInstances(t, r)
|
||||
}
|
||||
}
|
@ -0,0 +1,325 @@
|
||||
/*
|
||||
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 transient implements a transient deployment repository.
|
||||
package transient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kubernetes/deployment-manager/common"
|
||||
"github.com/kubernetes/deployment-manager/manager/repository"
|
||||
)
|
||||
|
||||
// deploymentTypeInstanceMap stores type instances mapped by deployment name.
|
||||
// This allows for simple updating and deleting of per-deployment instances
|
||||
// when deployments are created/updated/deleted.
|
||||
type deploymentTypeInstanceMap map[string][]*common.TypeInstance
|
||||
|
||||
type tRepository struct {
|
||||
sync.RWMutex
|
||||
deployments map[string]common.Deployment
|
||||
manifests map[string]map[string]*common.Manifest
|
||||
instances map[string]deploymentTypeInstanceMap
|
||||
}
|
||||
|
||||
// NewRepository returns a new transient repository. Its lifetime is coupled
|
||||
// to the lifetime of the current process. When the process dies, its contents
|
||||
// will be permanently destroyed.
|
||||
func NewRepository() repository.Repository {
|
||||
return &tRepository{
|
||||
deployments: make(map[string]common.Deployment, 0),
|
||||
manifests: make(map[string]map[string]*common.Manifest, 0),
|
||||
instances: make(map[string]deploymentTypeInstanceMap, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *tRepository) Close() {
|
||||
r.deployments = make(map[string]common.Deployment, 0)
|
||||
r.manifests = make(map[string]map[string]*common.Manifest, 0)
|
||||
r.instances = make(map[string]deploymentTypeInstanceMap, 0)
|
||||
}
|
||||
|
||||
// ListDeployments returns of all of the deployments in the repository.
|
||||
func (r *tRepository) ListDeployments() ([]common.Deployment, error) {
|
||||
l := []common.Deployment{}
|
||||
for _, deployment := range r.deployments {
|
||||
l = append(l, deployment)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// GetDeployment returns the deployment with the supplied name.
|
||||
// If the deployment is not found, it returns an error.
|
||||
func (r *tRepository) GetDeployment(name string) (*common.Deployment, error) {
|
||||
d, ok := r.deployments[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("deployment %s not found", name)
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// GetValidDeployment returns the deployment with the supplied name.
|
||||
// If the deployment is not found or marked as deleted, it returns an error.
|
||||
func (r *tRepository) GetValidDeployment(name string) (*common.Deployment, error) {
|
||||
d, err := r.GetDeployment(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if d.State.Status == common.DeletedStatus {
|
||||
return nil, fmt.Errorf("deployment %s is deleted", name)
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// SetDeploymentState sets the DeploymentState of the deployment and updates ModifiedAt
|
||||
func (r *tRepository) SetDeploymentState(name string, state *common.DeploymentState) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
d, err := r.GetValidDeployment(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.State = state
|
||||
d.ModifiedAt = time.Now()
|
||||
r.deployments[name] = *d
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateDeployment creates a new deployment and stores it in the repository.
|
||||
func (r *tRepository) CreateDeployment(name string) (*common.Deployment, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
exists, _ := r.GetValidDeployment(name)
|
||||
if exists != nil {
|
||||
return nil, fmt.Errorf("Deployment %s already exists", name)
|
||||
}
|
||||
|
||||
d := common.NewDeployment(name)
|
||||
r.deployments[name] = *d
|
||||
|
||||
log.Printf("created deployment: %v", d)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// AddManifest adds a manifest to the repository and repoints the latest
|
||||
// manifest to it for the corresponding deployment.
|
||||
func (r *tRepository) AddManifest(manifest *common.Manifest) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
deploymentName := manifest.Deployment
|
||||
l, err := r.ListManifests(deploymentName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure the manifest doesn't already exist, and if not, add the manifest to
|
||||
// map of manifests this deployment has
|
||||
if _, ok := l[manifest.Name]; ok {
|
||||
return fmt.Errorf("Manifest %s already exists in deployment %s", manifest.Name, deploymentName)
|
||||
}
|
||||
|
||||
d, err := r.GetValidDeployment(deploymentName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l[manifest.Name] = manifest
|
||||
d.LatestManifest = manifest.Name
|
||||
d.ModifiedAt = time.Now()
|
||||
r.deployments[deploymentName] = *d
|
||||
|
||||
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 *tRepository) SetManifest(manifest *common.Manifest) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
l, err := r.ListManifests(manifest.Deployment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, ok := l[manifest.Name]; !ok {
|
||||
return fmt.Errorf("manifest %s not found", manifest.Name)
|
||||
}
|
||||
|
||||
l[manifest.Name] = manifest
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteDeployment deletes the deployment with the supplied name.
|
||||
// If forget is true, then the deployment is removed from the repository.
|
||||
// Otherwise, it is marked as deleted and retained.
|
||||
func (r *tRepository) DeleteDeployment(name string, forget bool) (*common.Deployment, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
d, err := r.GetValidDeployment(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !forget {
|
||||
d.DeletedAt = time.Now()
|
||||
d.State = &common.DeploymentState{Status: common.DeletedStatus}
|
||||
r.deployments[name] = *d
|
||||
} else {
|
||||
delete(r.deployments, name)
|
||||
delete(r.manifests, name)
|
||||
d.LatestManifest = ""
|
||||
}
|
||||
|
||||
log.Printf("deleted deployment: %v", d)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (r *tRepository) ListManifests(deploymentName string) (map[string]*common.Manifest, error) {
|
||||
_, err := r.GetValidDeployment(deploymentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.listManifestsForDeployment(deploymentName)
|
||||
}
|
||||
|
||||
func (r *tRepository) listManifestsForDeployment(deploymentName string) (map[string]*common.Manifest, error) {
|
||||
l, ok := r.manifests[deploymentName]
|
||||
if !ok {
|
||||
l = make(map[string]*common.Manifest, 0)
|
||||
r.manifests[deploymentName] = l
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (r *tRepository) GetManifest(deploymentName string, manifestName string) (*common.Manifest, error) {
|
||||
_, err := r.GetValidDeployment(deploymentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.getManifestForDeployment(deploymentName, manifestName)
|
||||
}
|
||||
|
||||
func (r *tRepository) getManifestForDeployment(deploymentName string, manifestName string) (*common.Manifest, error) {
|
||||
l, err := r.listManifestsForDeployment(deploymentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m, ok := l[manifestName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("manifest %s not found in deployment %s", manifestName, deploymentName)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GetLatestManifest returns the latest manifest for a given deployment,
|
||||
// which by definition is the manifest with the largest time stamp.
|
||||
func (r *tRepository) GetLatestManifest(deploymentName string) (*common.Manifest, error) {
|
||||
d, err := r.GetValidDeployment(deploymentName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if d.LatestManifest == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return r.getManifestForDeployment(deploymentName, d.LatestManifest)
|
||||
}
|
||||
|
||||
// ListTypes returns all types known from existing instances.
|
||||
func (r *tRepository) ListTypes() ([]string, error) {
|
||||
var keys []string
|
||||
for k := range r.instances {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// GetTypeInstances returns all instances of a given type. If type is empty,
|
||||
// returns all instances for all types.
|
||||
func (r *tRepository) GetTypeInstances(typeName string) ([]*common.TypeInstance, error) {
|
||||
var instances []*common.TypeInstance
|
||||
for t, dInstMap := range r.instances {
|
||||
if t == typeName || typeName == "" || typeName == "all" {
|
||||
for _, i := range dInstMap {
|
||||
instances = append(instances, i...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instances, nil
|
||||
}
|
||||
|
||||
// ClearTypeInstancesForDeployment deletes all type instances associated with the given
|
||||
// deployment from the repository.
|
||||
func (r *tRepository) ClearTypeInstancesForDeployment(deploymentName string) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
for t, dMap := range r.instances {
|
||||
delete(dMap, deploymentName)
|
||||
if len(dMap) == 0 {
|
||||
delete(r.instances, t)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTypeInstances adds the supplied type instances to the repository.
|
||||
func (r *tRepository) AddTypeInstances(instances map[string][]*common.TypeInstance) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
// Add instances to the appropriate type and deployment maps.
|
||||
for t, is := range instances {
|
||||
if r.instances[t] == nil {
|
||||
r.instances[t] = make(deploymentTypeInstanceMap)
|
||||
}
|
||||
|
||||
tmap := r.instances[t]
|
||||
for _, instance := range is {
|
||||
deployment := instance.Deployment
|
||||
if tmap[deployment] == nil {
|
||||
tmap[deployment] = make([]*common.TypeInstance, 0)
|
||||
}
|
||||
|
||||
tmap[deployment] = append(tmap[deployment], instance)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
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 transient
|
||||
|
||||
import (
|
||||
"github.com/kubernetes/deployment-manager/manager/repository"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRepositoryListEmpty(t *testing.T) {
|
||||
repository.TestRepositoryListEmpty(t, NewRepository())
|
||||
}
|
||||
|
||||
func TestRepositoryGetFailsWithNonExistentDeployment(t *testing.T) {
|
||||
repository.TestRepositoryGetFailsWithNonExistentDeployment(t, NewRepository())
|
||||
}
|
||||
|
||||
func TestRepositoryCreateDeploymentWorks(t *testing.T) {
|
||||
repository.TestRepositoryCreateDeploymentWorks(t, NewRepository())
|
||||
}
|
||||
|
||||
func TestRepositoryMultipleManifestsWorks(t *testing.T) {
|
||||
repository.TestRepositoryMultipleManifestsWorks(t, NewRepository())
|
||||
}
|
||||
|
||||
func TestRepositoryDeleteFailsWithNonExistentDeployment(t *testing.T) {
|
||||
repository.TestRepositoryDeleteFailsWithNonExistentDeployment(t, NewRepository())
|
||||
}
|
||||
|
||||
func TestRepositoryDeleteWorksWithNoLatestManifest(t *testing.T) {
|
||||
repository.TestRepositoryDeleteWorksWithNoLatestManifest(t, NewRepository())
|
||||
}
|
||||
|
||||
func TestRepositoryDeleteDeploymentWorksNoForget(t *testing.T) {
|
||||
repository.TestRepositoryDeleteDeploymentWorksNoForget(t, NewRepository())
|
||||
}
|
||||
|
||||
func TestRepositoryDeleteDeploymentWorksForget(t *testing.T) {
|
||||
repository.TestRepositoryDeleteDeploymentWorksForget(t, NewRepository())
|
||||
}
|
||||
|
||||
func TestRepositoryTypeInstances(t *testing.T) {
|
||||
repository.TestRepositoryTypeInstances(t, NewRepository())
|
||||
}
|
Loading…
Reference in new issue