Files
kubesphere/pkg/models/openpitrix/applications.go
hongming 71849f028f [WIP] API refactor (#1737)
* refactor openpitrix API

Signed-off-by: hongming <talonwan@yunify.com>

* add openpitrix mock client

Signed-off-by: hongming <talonwan@yunify.com>

* refactor tenant API

Signed-off-by: hongming <talonwan@yunify.com>

* refactor IAM API

Signed-off-by: hongming <talonwan@yunify.com>

* refactor IAM API

Signed-off-by: hongming <talonwan@yunify.com>
2020-01-13 13:36:21 +08:00

395 lines
12 KiB
Go

/*
*
* Copyright 2020 The KubeSphere Authors.
*
* 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 openpitrix
import (
"fmt"
"github.com/golang/protobuf/ptypes/wrappers"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/informers"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"openpitrix.io/openpitrix/pkg/pb"
"strings"
)
type ApplicationInterface interface {
ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error)
DescribeApplication(namespace, clusterId string) (*Application, error)
CreateApplication(namespace string, request CreateClusterRequest) error
ModifyApplication(request ModifyClusterAttributesRequest) error
DeleteApplication(id string) error
}
type applicationOperator struct {
informers informers.SharedInformerFactory
opClient openpitrix.Client
}
func newApplicationOperator(informers informers.SharedInformerFactory, opClient openpitrix.Client) ApplicationInterface {
return &applicationOperator{informers: informers, opClient: opClient}
}
type Application struct {
Name string `json:"name" description:"application name"`
Cluster *Cluster `json:"cluster,omitempty" description:"application cluster info"`
Version *AppVersion `json:"version,omitempty" description:"application template version info"`
App *App `json:"app,omitempty" description:"application template info"`
WorkLoads *workLoads `json:"workloads,omitempty" description:"application workloads"`
Services []v1.Service `json:"services,omitempty" description:"application services"`
Ingresses []v1beta1.Ingress `json:"ingresses,omitempty" description:"application ingresses"`
}
type workLoads struct {
Deployments []appsv1.Deployment `json:"deployments,omitempty" description:"deployment list"`
Statefulsets []appsv1.StatefulSet `json:"statefulsets,omitempty" description:"statefulset list"`
Daemonsets []appsv1.DaemonSet `json:"daemonsets,omitempty" description:"daemonset list"`
}
func (c *applicationOperator) ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) {
describeClustersRequest := &pb.DescribeClustersRequest{
Limit: uint32(limit),
Offset: uint32(offset)}
if keyword := conditions.Match[Keyword]; keyword != "" {
describeClustersRequest.SearchWord = &wrappers.StringValue{Value: keyword}
}
if runtimeId := conditions.Match[RuntimeId]; runtimeId != "" {
describeClustersRequest.RuntimeId = []string{runtimeId}
}
if appId := conditions.Match[AppId]; appId != "" {
describeClustersRequest.AppId = []string{appId}
}
if versionId := conditions.Match[VersionId]; versionId != "" {
describeClustersRequest.VersionId = []string{versionId}
}
if status := conditions.Match[Status]; status != "" {
describeClustersRequest.Status = strings.Split(status, "|")
}
if orderBy != "" {
describeClustersRequest.SortKey = &wrappers.StringValue{Value: orderBy}
}
describeClustersRequest.Reverse = &wrappers.BoolValue{Value: reverse}
resp, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), describeClustersRequest)
if err != nil {
klog.Errorln(err)
return nil, err
}
result := models.PageableResponse{TotalCount: int(resp.TotalCount)}
result.Items = make([]interface{}, 0)
for _, cluster := range resp.ClusterSet {
app, err := c.describeApplication(cluster)
if err != nil {
klog.Errorln(err)
return nil, err
}
result.Items = append(result.Items, app)
}
return &result, nil
}
func (c *applicationOperator) describeApplication(cluster *pb.Cluster) (*Application, error) {
var app Application
app.Name = cluster.Name.Value
app.Cluster = convertCluster(cluster)
versionInfo, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{VersionId: []string{cluster.GetVersionId().GetValue()}})
if err != nil {
klog.Errorln(err)
return nil, err
}
if len(versionInfo.AppVersionSet) > 0 {
app.Version = convertAppVersion(versionInfo.AppVersionSet[0])
}
appInfo, err := c.opClient.DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{AppId: []string{cluster.GetAppId().GetValue()}, Limit: 1})
if err != nil {
klog.Errorln(err)
return nil, err
}
if len(appInfo.AppSet) > 0 {
app.App = convertApp(appInfo.GetAppSet()[0])
}
return &app, nil
}
func (c *applicationOperator) DescribeApplication(namespace string, clusterId string) (*Application, error) {
clusters, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), &pb.DescribeClustersRequest{ClusterId: []string{clusterId}, Limit: 1})
if err != nil {
klog.Errorln(err)
return nil, err
}
var cluster *pb.Cluster
if len(clusters.ClusterSet) > 0 {
cluster = clusters.GetClusterSet()[0]
} else {
err := status.New(codes.NotFound, "resource not found").Err()
klog.Errorln(err)
return nil, err
}
app, err := c.describeApplication(cluster)
if err != nil {
klog.Errorln(err)
return nil, err
}
workloads, err := c.getWorkLoads(namespace, cluster.ClusterRoleSet)
if err != nil {
klog.Errorln(err)
return nil, err
}
app.WorkLoads = workloads
workloadLabels := c.getLabels(namespace, app.WorkLoads)
app.Services = c.getSvcs(namespace, workloadLabels)
app.Ingresses = c.getIng(namespace, app.Services)
return app, nil
}
func (c *applicationOperator) getWorkLoads(namespace string, clusterRoles []*pb.ClusterRole) (*workLoads, error) {
var works workLoads
for _, clusterRole := range clusterRoles {
workLoadName := clusterRole.Role.Value
if len(workLoadName) > 0 {
if strings.HasSuffix(workLoadName, openpitrix.DeploySuffix) {
name := strings.Split(workLoadName, openpitrix.DeploySuffix)[0]
item, err := c.informers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(name)
if err != nil {
// app not ready
if errors.IsNotFound(err) {
continue
}
klog.Errorln(err)
return nil, err
}
works.Deployments = append(works.Deployments, *item)
continue
}
if strings.HasSuffix(workLoadName, openpitrix.DaemonSuffix) {
name := strings.Split(workLoadName, openpitrix.DaemonSuffix)[0]
item, err := c.informers.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name)
if err != nil {
// app not ready
if errors.IsNotFound(err) {
continue
}
klog.Errorln(err)
return nil, err
}
works.Daemonsets = append(works.Daemonsets, *item)
continue
}
if strings.HasSuffix(workLoadName, openpitrix.StateSuffix) {
name := strings.Split(workLoadName, openpitrix.StateSuffix)[0]
item, err := c.informers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name)
if err != nil {
// app not ready
if errors.IsNotFound(err) {
continue
}
klog.Errorln(err)
return nil, err
}
works.Statefulsets = append(works.Statefulsets, *item)
continue
}
}
}
return &works, nil
}
func (c *applicationOperator) getLabels(namespace string, workloads *workLoads) *[]map[string]string {
var workloadLabels []map[string]string
if workloads == nil {
return nil
}
for _, workload := range workloads.Deployments {
deploy, err := c.informers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(workload.Name)
if errors.IsNotFound(err) {
continue
}
workloadLabels = append(workloadLabels, deploy.Labels)
}
for _, workload := range workloads.Daemonsets {
daemonset, err := c.informers.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(workload.Name)
if errors.IsNotFound(err) {
continue
}
workloadLabels = append(workloadLabels, daemonset.Labels)
}
for _, workload := range workloads.Statefulsets {
statefulset, err := c.informers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(workload.Name)
if errors.IsNotFound(err) {
continue
}
workloadLabels = append(workloadLabels, statefulset.Labels)
}
return &workloadLabels
}
func (c *applicationOperator) isExist(svcs []v1.Service, svc *v1.Service) bool {
for _, item := range svcs {
if item.Name == svc.Name && item.Namespace == svc.Namespace {
return true
}
}
return false
}
func (c *applicationOperator) getSvcs(namespace string, workLoadLabels *[]map[string]string) []v1.Service {
if len(*workLoadLabels) == 0 {
return nil
}
var services []v1.Service
for _, label := range *workLoadLabels {
labelSelector := labels.Set(label).AsSelector()
svcs, err := c.informers.Core().V1().Services().Lister().Services(namespace).List(labelSelector)
if err != nil {
klog.Errorf("get app's svc failed, reason: %v", err)
}
for _, item := range svcs {
if !c.isExist(services, item) {
services = append(services, *item)
}
}
}
return services
}
func (c *applicationOperator) getIng(namespace string, services []v1.Service) []v1beta1.Ingress {
if services == nil {
return nil
}
var ings []v1beta1.Ingress
for _, svc := range services {
ingresses, err := c.informers.Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).List(labels.Everything())
if err != nil {
klog.Error(err)
return ings
}
for _, ingress := range ingresses {
if ingress.Spec.Backend.ServiceName != svc.Name {
continue
}
exist := false
var tmpRules []v1beta1.IngressRule
for _, rule := range ingress.Spec.Rules {
for _, p := range rule.HTTP.Paths {
if p.Backend.ServiceName == svc.Name {
exist = true
tmpRules = append(tmpRules, rule)
}
}
}
if exist {
ing := v1beta1.Ingress{}
ing.Name = ingress.Name
ing.Spec.Rules = tmpRules
ings = append(ings, ing)
}
}
}
return ings
}
func (c *applicationOperator) CreateApplication(namespace string, request CreateClusterRequest) error {
ns, err := c.informers.Core().V1().Namespaces().Lister().Get(namespace)
if err != nil {
klog.Error(err)
return err
}
if runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" {
request.RuntimeId = runtimeId
} else {
return fmt.Errorf("runtime not init: namespace %s", namespace)
}
_, err = c.opClient.CreateCluster(openpitrix.ContextWithUsername(request.Username), &pb.CreateClusterRequest{
AppId: &wrappers.StringValue{Value: request.AppId},
VersionId: &wrappers.StringValue{Value: request.VersionId},
RuntimeId: &wrappers.StringValue{Value: request.RuntimeId},
Conf: &wrappers.StringValue{Value: request.Conf},
})
if err != nil {
klog.Errorln(err)
return err
}
return nil
}
func (c *applicationOperator) ModifyApplication(request ModifyClusterAttributesRequest) error {
modifyClusterAttributesRequest := &pb.ModifyClusterAttributesRequest{ClusterId: &wrappers.StringValue{Value: request.ClusterID}}
if request.Name != nil {
modifyClusterAttributesRequest.Name = &wrappers.StringValue{Value: *request.Name}
}
if request.Description != nil {
modifyClusterAttributesRequest.Description = &wrappers.StringValue{Value: *request.Description}
}
_, err := c.opClient.ModifyClusterAttributes(openpitrix.SystemContext(), modifyClusterAttributesRequest)
if err != nil {
klog.Errorln(err)
return err
}
return nil
}
func (c *applicationOperator) DeleteApplication(clusterId string) error {
_, err := c.opClient.DeleteClusters(openpitrix.SystemContext(), &pb.DeleteClustersRequest{ClusterId: []string{clusterId}, Force: &wrappers.BoolValue{Value: true}})
if err != nil {
klog.Errorln(err)
return err
}
return nil
}