package cluster
import (
"context"
"fmt"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"strings"
"github.com/docker/distribution/reference"
"github.com/goodrain/rainbond-operator/api/v1alpha1"
"github.com/goodrain/rainbond/grctl/clients"
"github.com/goodrain/rainbond/util/apply"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/yaml"
)
type Cluster struct {
rainbondCluster *v1alpha1.RainbondCluster
namespace string
newVersion string
}
func NewCluster(namespace, newVersion string) (*Cluster, error) {
rainbondCluster, err := getRainbondCluster(namespace)
if err != nil {
return nil, err
}
return &Cluster{
rainbondCluster: rainbondCluster,
namespace: namespace,
newVersion: newVersion,
}, nil
}
func (c *Cluster) Upgrade() error {
logrus.Infof("upgrade cluster from %s to %s", c.rainbondCluster.Spec.InstallVersion, c.newVersion)
if errs := c.createCrds(); len(errs) > 0 {
return errors.New(strings.Join(errs, ","))
}
if errs := c.updateRbdComponents(); len(errs) > 0 {
return fmt.Errorf("update rainbond components: %s", strings.Join(errs, ","))
}
if err := c.updateCluster(); err != nil {
return err
}
return nil
}
func (c *Cluster) createCrds() []string {
crds := c.getCrds()
if crds == nil {
return nil
}
logrus.Info("start creating crds")
var errs []string
for _, crd := range crds {
if err := c.createCrd(crd); err != nil {
errs = append(errs, err.Error())
}
}
logrus.Info("crds applyed")
return errs
}
func (c *Cluster) createCrd(crdStr string) error {
var crd apiextensionsv1.CustomResourceDefinition
if err := yaml.Unmarshal([]byte(crdStr), &crd); err != nil {
return fmt.Errorf("unmarshal crd: %v", err)
}
applyer := apply.NewAPIApplicator(clients.RainbondKubeClient)
if err := applyer.Apply(context.Background(), &crd); err != nil {
return fmt.Errorf("apply crd: %v", err)
}
return nil
}
func (c *Cluster) getCrds() []string {
for v, versionConfig := range versions {
if strings.Contains(c.newVersion, v) {
return versionConfig.CRDs
}
}
return nil
}
func (c *Cluster) updateRbdComponents() []string {
componentNames := []string{
"rbd-api",
"rbd-chaos",
"rbd-mq",
"rbd-eventlog",
"rbd-gateway",
"rbd-node",
"rbd-resource-proxy",
"rbd-webcli",
"rbd-worker",
"rbd-monitor",
"rbd-app-ui",
}
var errs []string
for _, name := range componentNames {
err := c.updateRbdComponent(name)
if err != nil {
errs = append(errs, err.Error())
}
}
return errs
}
func (c *Cluster) updateRbdComponent(name string) error {
var cpt v1alpha1.RbdComponent
err := clients.RainbondKubeClient.Get(context.Background(),
types.NamespacedName{Namespace: c.namespace, Name: name}, &cpt)
if err != nil {
return fmt.Errorf("get rbdcomponent %s: %v", name, err)
}
ref, err := reference.Parse(cpt.Spec.Image)
if err != nil {
return fmt.Errorf("parse image %s: %v", cpt.Spec.Image, err)
}
repo := ref.(reference.Named)
newImage := repo.Name() + ":" + c.newVersion
oldImageName := cpt.Spec.Image
cpt.Spec.Image = newImage
if err := clients.RainbondKubeClient.Update(context.Background(), &cpt); err != nil {
return fmt.Errorf("update rbdcomponent %s: %v", name, err)
}
logrus.Infof("update rbdcomponent %s \nfrom %s \nto %s", name, oldImageName, newImage)
return nil
}
func (c *Cluster) updateCluster() error {
c.rainbondCluster.Spec.InstallVersion = c.newVersion
if err := clients.RainbondKubeClient.Update(context.Background(), c.rainbondCluster); err != nil {
return fmt.Errorf("update rainbond cluster: %v", err)
}
logrus.Infof("update rainbond cluster to %s", c.newVersion)
return nil
}
func getRainbondCluster(namespace string) (*v1alpha1.RainbondCluster, error) {
var cluster v1alpha1.RainbondCluster
err := clients.RainbondKubeClient.Get(context.Background(),
types.NamespacedName{Namespace: namespace, Name: "rainbondcluster"}, &cluster)
if err != nil {
return nil, fmt.Errorf("get rainbond cluster: %v", err)
}
return &cluster, nil
}