forked from rook/rook
189 lines
5.7 KiB
Go
189 lines
5.7 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 cluster
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
"github.com/coreos/pkg/capnslog"
|
|
"github.com/rook/rook/pkg/cephmgr/client"
|
|
cephmon "github.com/rook/rook/pkg/cephmgr/mon"
|
|
"github.com/rook/rook/pkg/clusterd"
|
|
"github.com/rook/rook/pkg/operator/api"
|
|
"github.com/rook/rook/pkg/operator/k8sutil"
|
|
"github.com/rook/rook/pkg/operator/mon"
|
|
"github.com/rook/rook/pkg/operator/osd"
|
|
"github.com/rook/rook/pkg/operator/rgw"
|
|
rookclient "github.com/rook/rook/pkg/rook/client"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/client-go/pkg/api/v1"
|
|
)
|
|
|
|
var (
|
|
healthCheckInterval = 10 * time.Second
|
|
clientTimeout = 15 * time.Second
|
|
logger = capnslog.NewPackageLogger("github.com/rook/rook", "op-cluster")
|
|
)
|
|
|
|
type Cluster struct {
|
|
context *k8sutil.Context
|
|
v1.ObjectMeta `json:"metadata,omitempty"`
|
|
Spec `json:"spec"`
|
|
dataDir string
|
|
mons *mon.Cluster
|
|
osds *osd.Cluster
|
|
apis *api.Cluster
|
|
rgws *rgw.Cluster
|
|
rclient rookclient.RookRestClient
|
|
}
|
|
|
|
func (c *Cluster) Init(context *k8sutil.Context) {
|
|
c.context = context
|
|
c.dataDir = k8sutil.DataDir
|
|
}
|
|
|
|
func (c *Cluster) CreateInstance() error {
|
|
|
|
// Create the namespace if not already created
|
|
ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: c.Namespace}}
|
|
|
|
_, err := c.context.Clientset.CoreV1().Namespaces().Create(ns)
|
|
if err != nil {
|
|
if !errors.IsAlreadyExists(err) {
|
|
return fmt.Errorf("failed to create namespace %s. %+v", c.Namespace, err)
|
|
}
|
|
}
|
|
|
|
// Start the mon pods
|
|
c.mons = mon.New(c.context, c.Name, c.Namespace, c.Spec.DataDirHostPath, c.Spec.VersionTag)
|
|
clusterInfo, err := c.mons.Start()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to start the mons. %+v", err)
|
|
}
|
|
|
|
c.apis = api.New(c.context, c.Name, c.Namespace, c.Spec.VersionTag)
|
|
err = c.apis.Start()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to start the REST api. %+v", err)
|
|
}
|
|
|
|
// Start the OSDs
|
|
c.osds = osd.New(c.context, c.Name, c.Namespace, c.Spec.VersionTag, c.Spec.Storage, c.Spec.DataDirHostPath)
|
|
err = c.osds.Start()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to start the osds. %+v", err)
|
|
}
|
|
|
|
err = c.createClientAccess(clusterInfo)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create client access. %+v", err)
|
|
}
|
|
|
|
logger.Infof("Done creating rook instance in namespace %s", c.Namespace)
|
|
return nil
|
|
}
|
|
|
|
func (c *Cluster) Monitor(stopCh <-chan struct{}) {
|
|
for {
|
|
select {
|
|
case <-stopCh:
|
|
logger.Infof("Stopping monitoring of cluster %s in namespace %s", c.Name, c.Namespace)
|
|
return
|
|
|
|
case <-time.After(healthCheckInterval):
|
|
logger.Debugf("checking health of mons")
|
|
err := c.mons.CheckHealth()
|
|
if err != nil {
|
|
logger.Infof("failed to check mon health. %+v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Cluster) createClientAccess(clusterInfo *cephmon.ClusterInfo) error {
|
|
ctx := &clusterd.Context{ConfigDir: c.dataDir}
|
|
conn, err := cephmon.ConnectToClusterAsAdmin(ctx, c.context.Factory, clusterInfo)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to connect to cluster: %+v", err)
|
|
}
|
|
defer conn.Shutdown()
|
|
|
|
// create a user for rbd clients
|
|
name := fmt.Sprintf("%s-rbd-user", c.Name)
|
|
username := fmt.Sprintf("client.%s", name)
|
|
access := []string{"osd", "allow rwx", "mon", "allow r"}
|
|
|
|
// get-or-create-key for the user account
|
|
rbdKey, err := client.AuthGetOrCreateKey(conn, username, access)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get or create auth key for %s. %+v", username, err)
|
|
}
|
|
|
|
// store the secret for the rbd user in the default namespace
|
|
secrets := map[string]string{
|
|
"key": rbdKey,
|
|
}
|
|
secret := &v1.Secret{
|
|
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: k8sutil.DefaultNamespace},
|
|
StringData: secrets,
|
|
Type: k8sutil.RbdType,
|
|
}
|
|
_, err = c.context.Clientset.CoreV1().Secrets(k8sutil.DefaultNamespace).Create(secret)
|
|
if err != nil {
|
|
if !errors.IsAlreadyExists(err) {
|
|
return fmt.Errorf("failed to save %s secret. %+v", name, err)
|
|
}
|
|
|
|
// update the secret in case we have a new cluster
|
|
_, err = c.context.Clientset.CoreV1().Secrets(k8sutil.DefaultNamespace).Update(secret)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update %s secret. %+v", name, err)
|
|
}
|
|
logger.Infof("updated existing %s secret", name)
|
|
} else {
|
|
logger.Infof("saved %s secret", name)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Cluster) GetRookClient() (rookclient.RookRestClient, error) {
|
|
if c.rclient != nil {
|
|
return c.rclient, nil
|
|
}
|
|
|
|
// Look up the api service for the given namespace
|
|
logger.Infof("retrieving rook api endpoint for namespace %s", c.Namespace)
|
|
svc, err := c.context.Clientset.CoreV1().Services(c.Namespace).Get(api.DeploymentName, metav1.GetOptions{})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find the api service. %+v", err)
|
|
}
|
|
|
|
httpClient := http.DefaultClient
|
|
httpClient.Timeout = clientTimeout
|
|
endpoint := fmt.Sprintf("%s:%d", svc.Spec.ClusterIP, svc.Spec.Ports[0].Port)
|
|
c.rclient = rookclient.NewRookNetworkRestClient(rookclient.GetRestURL(endpoint), httpClient)
|
|
logger.Infof("rook api endpoint %s for namespace %s", endpoint, c.Namespace)
|
|
return c.rclient, nil
|
|
}
|