Files
my-rook-config/pkg/operator/pool/controller.go
T
Steve Leon b4279e06fb Rook plugin for Kubernetes implemented as flexvolume
- Operator is deployed under namespace rook-system
- Operator deploys rook-agent on all nodes as daemonset also on
  rook-system
- Rook-agent installs flexvolume driver on hosts
- Rook-agent listens on unix socket for driver requests
- Rook-agent perform attach/detach on its node
- Rook-agent creates/delete CRD volumeattach objects
- Added fencing to support ROX and RWO
- Added unit and integration tests
- Updated examples and docks

fixes #432
2017-10-09 09:14:08 -07:00

247 lines
6.9 KiB
Go

/*
Copyright 2016 The Rook 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.
Some of the code below came from https://github.com/coreos/etcd-operator
which also has the apache 2.0 license.
*/
// Package pool to manage a rook pool.
package pool
import (
"fmt"
"github.com/coreos/pkg/capnslog"
ceph "github.com/rook/rook/pkg/ceph/client"
"github.com/rook/rook/pkg/clusterd"
"github.com/rook/rook/pkg/model"
"github.com/rook/rook/pkg/operator/k8sutil"
"github.com/rook/rook/pkg/operator/kit"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/cache"
)
const (
customResourceName = "pool"
customResourceNamePlural = "pools"
replicatedType = "replicated"
erasureCodeType = "erasure-coded"
)
var logger = capnslog.NewPackageLogger("github.com/rook/rook", "op-pool")
// PoolController represents a controller object for pool custom resources
type PoolController struct {
context *clusterd.Context
scheme *runtime.Scheme
}
// NewPoolController create controller for watching pool custom resources created
func NewPoolController(context *clusterd.Context) *PoolController {
return &PoolController{
context: context,
}
}
// Watch watches for instances of Pool custom resources and acts on them
func (c *PoolController) StartWatch(namespace string, stopCh chan struct{}) error {
client, scheme, err := kit.NewHTTPClient(k8sutil.CustomResourceGroup, k8sutil.V1Alpha1, schemeBuilder)
if err != nil {
return fmt.Errorf("failed to get a k8s client for watching pool resources: %v", err)
}
c.scheme = scheme
resourceHandlerFuncs := cache.ResourceEventHandlerFuncs{
AddFunc: c.onAdd,
UpdateFunc: c.onUpdate,
DeleteFunc: c.onDelete,
}
watcher := kit.NewWatcher(PoolResource, namespace, resourceHandlerFuncs, client)
go watcher.Watch(&Pool{}, stopCh)
return nil
}
func (c *PoolController) onAdd(obj interface{}) {
pool := obj.(*Pool)
// NEVER modify objects from the store. It's a read-only, local cache.
// Use scheme.Copy() to make a deep copy of original object.
copyObj, err := c.scheme.Copy(pool)
if err != nil {
logger.Errorf("failed to create a deep copy of pool object: %v\n", err)
return
}
poolCopy := copyObj.(*Pool)
err = poolCopy.create(c.context)
if err != nil {
logger.Errorf("failed to create pool %s. %+v", pool.ObjectMeta.Name, err)
}
}
func (c *PoolController) onUpdate(oldObj, newObj interface{}) {
oldPool := oldObj.(*Pool)
pool := newObj.(*Pool)
if oldPool.Name != pool.Name {
logger.Errorf("failed to update pool %s. name update not allowed", pool.Name)
return
}
if pool.Spec.ErasureCoded.CodingChunks != 0 && pool.Spec.ErasureCoded.DataChunks != 0 {
logger.Errorf("failed to update pool %s. erasurecoded update not allowed", pool.Name)
return
}
// if the pool is modified, allow the pool to be created if it wasn't already
if err := pool.create(c.context); err != nil {
logger.Errorf("failed to create (modify) pool %s. %+v", pool.ObjectMeta.Name, err)
}
}
func (c *PoolController) onDelete(obj interface{}) {
pool := obj.(*Pool)
if err := pool.delete(c.context); err != nil {
logger.Errorf("failed to delete pool %s. %+v", pool.ObjectMeta.Name, err)
}
}
// Create the pool
func (p *Pool) create(context *clusterd.Context) error {
// validate the pool settings
if err := p.validate(context); err != nil {
return fmt.Errorf("invalid pool %s arguments. %+v", p.Name, err)
}
// create the pool
logger.Infof("creating pool %s in namespace %s", p.Name, p.Namespace)
if err := ceph.CreatePoolWithProfile(context, p.Namespace, *p.Spec.ToModel(p.Name), p.Name); err != nil {
return fmt.Errorf("failed to create pool %s. %+v", p.Name, err)
}
logger.Infof("created pool %s", p.Name)
return nil
}
// Delete the pool
func (p *Pool) delete(context *clusterd.Context) error {
// check if the pool exists
exists, err := p.exists(context)
if err == nil && !exists {
return nil
}
logger.Infof("TODO: delete pool %s from namespace %s", p.Name, p.Namespace)
//return p.client.DeletePool(p.PoolSpec.Name)
return nil
}
// Check if the pool exists
func (p *Pool) exists(context *clusterd.Context) (bool, error) {
pools, err := ceph.GetPools(context, p.Namespace)
if err != nil {
return false, err
}
for _, pool := range pools {
if pool.Name == p.Name {
return true, nil
}
}
return false, nil
}
// Validate the pool arguments
func (p *Pool) validate(context *clusterd.Context) error {
if p.Name == "" {
return fmt.Errorf("missing name")
}
if p.Namespace == "" {
return fmt.Errorf("missing namespace")
}
if err := p.Spec.Validate(context, p.Namespace); err != nil {
return err
}
return nil
}
func (p *PoolSpec) ToModel(name string) *model.Pool {
pool := &model.Pool{Name: name, FailureDomain: p.FailureDomain}
r := p.replication()
if r != nil {
pool.ReplicatedConfig.Size = r.Size
pool.Type = model.Replicated
} else {
ec := p.erasureCode()
if ec != nil {
pool.ErasureCodedConfig.CodingChunkCount = ec.CodingChunks
pool.ErasureCodedConfig.DataChunkCount = ec.DataChunks
pool.Type = model.ErasureCoded
}
}
return pool
}
func (p *PoolSpec) replication() *ReplicatedSpec {
if p.Replicated.Size > 0 {
return &p.Replicated
}
return nil
}
func (p *PoolSpec) erasureCode() *ErasureCodedSpec {
ec := &p.ErasureCoded
if ec.CodingChunks > 0 || ec.DataChunks > 0 {
return ec
}
return nil
}
func (p *PoolSpec) Validate(context *clusterd.Context, namespace string) error {
if p.replication() != nil && p.erasureCode() != nil {
return fmt.Errorf("both replication and erasure code settings cannot be specified")
}
if p.replication() == nil && p.erasureCode() == nil {
return fmt.Errorf("neither replication nor erasure code settings were specified")
}
// validate the failure domain if specified
if p.FailureDomain != "" {
crush, err := ceph.GetCrushMap(context, namespace)
if err != nil {
return fmt.Errorf("failed to get crush map. %+v", err)
}
found := false
for _, t := range crush.Types {
if t.Name == p.FailureDomain {
found = true
break
}
}
if !found {
return fmt.Errorf("unrecognized failure domain %s", p.FailureDomain)
}
}
return nil
}
func ModelToSpec(pool model.Pool) PoolSpec {
ec := pool.ErasureCodedConfig
return PoolSpec{
FailureDomain: pool.FailureDomain,
Replicated: ReplicatedSpec{Size: pool.ReplicatedConfig.Size},
ErasureCoded: ErasureCodedSpec{CodingChunks: ec.CodingChunkCount, DataChunks: ec.DataChunkCount, Algorithm: ec.Algorithm},
}
}