applications.go 12.1 KB
Newer Older
Z
zryfish 已提交
1
/*
H
hongming 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * 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
Z
zryfish 已提交
19 20

import (
H
hongming 已提交
21
	"fmt"
H
hongming 已提交
22 23 24
	"github.com/golang/protobuf/ptypes/wrappers"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
H
hongming 已提交
25
	appsv1 "k8s.io/api/apps/v1"
Z
zryfish 已提交
26 27 28 29
	"k8s.io/api/core/v1"
	"k8s.io/api/extensions/v1beta1"
	"k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/labels"
Z
zryfish 已提交
30
	"k8s.io/client-go/informers"
J
Jeff 已提交
31
	"k8s.io/klog"
H
hongming 已提交
32
	"kubesphere.io/kubesphere/pkg/constants"
Z
zryfish 已提交
33
	"kubesphere.io/kubesphere/pkg/models"
J
Jeff 已提交
34
	"kubesphere.io/kubesphere/pkg/server/params"
H
hongming 已提交
35
	"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
H
hongming 已提交
36
	"openpitrix.io/openpitrix/pkg/pb"
Z
zryfish 已提交
37 38 39
	"strings"
)

H
hongming 已提交
40 41 42 43 44 45
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
Z
zryfish 已提交
46 47 48 49
}

type applicationOperator struct {
	informers informers.SharedInformerFactory
H
hongming 已提交
50
	opClient  openpitrix.Client
Z
zryfish 已提交
51 52
}

H
hongming 已提交
53 54
func newApplicationOperator(informers informers.SharedInformerFactory, opClient openpitrix.Client) ApplicationInterface {
	return &applicationOperator{informers: informers, opClient: opClient}
Z
zryfish 已提交
55 56
}

Z
zryfish 已提交
57
type Application struct {
H
hongming 已提交
58
	Name      string            `json:"name" description:"application name"`
H
hongming 已提交
59 60 61
	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"`
H
hongming 已提交
62 63 64
	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"`
Z
zryfish 已提交
65 66 67
}

type workLoads struct {
H
hongming 已提交
68 69 70
	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"`
Z
zryfish 已提交
71 72
}

H
hongming 已提交
73
func (c *applicationOperator) ListApplications(conditions *params.Conditions, limit, offset int, orderBy string, reverse bool) (*models.PageableResponse, error) {
H
hongming 已提交
74 75 76
	describeClustersRequest := &pb.DescribeClustersRequest{
		Limit:  uint32(limit),
		Offset: uint32(offset)}
H
hongming 已提交
77
	if keyword := conditions.Match[Keyword]; keyword != "" {
H
hongming 已提交
78 79
		describeClustersRequest.SearchWord = &wrappers.StringValue{Value: keyword}
	}
H
hongming 已提交
80
	if runtimeId := conditions.Match[RuntimeId]; runtimeId != "" {
H
hongming 已提交
81 82
		describeClustersRequest.RuntimeId = []string{runtimeId}
	}
H
hongming 已提交
83
	if appId := conditions.Match[AppId]; appId != "" {
H
hongming 已提交
84 85
		describeClustersRequest.AppId = []string{appId}
	}
H
hongming 已提交
86
	if versionId := conditions.Match[VersionId]; versionId != "" {
H
hongming 已提交
87 88
		describeClustersRequest.VersionId = []string{versionId}
	}
H
hongming 已提交
89
	if status := conditions.Match[Status]; status != "" {
H
hongming 已提交
90 91
		describeClustersRequest.Status = strings.Split(status, "|")
	}
H
hongming 已提交
92 93 94
	if orderBy != "" {
		describeClustersRequest.SortKey = &wrappers.StringValue{Value: orderBy}
	}
H
hongming 已提交
95 96
	describeClustersRequest.Reverse = &wrappers.BoolValue{Value: reverse}
	resp, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), describeClustersRequest)
Z
zryfish 已提交
97
	if err != nil {
H
hongming 已提交
98
		klog.Errorln(err)
H
hongming 已提交
99
		return nil, err
Z
zryfish 已提交
100
	}
H
hongming 已提交
101
	result := models.PageableResponse{TotalCount: int(resp.TotalCount)}
H
hongming 已提交
102
	result.Items = make([]interface{}, 0)
H
hongming 已提交
103
	for _, cluster := range resp.ClusterSet {
Z
zryfish 已提交
104
		app, err := c.describeApplication(cluster)
H
hongming 已提交
105 106 107 108
		if err != nil {
			klog.Errorln(err)
			return nil, err
		}
H
hongming 已提交
109
		result.Items = append(result.Items, app)
Z
zryfish 已提交
110 111
	}

H
hongming 已提交
112
	return &result, nil
Z
zryfish 已提交
113 114
}

Z
zryfish 已提交
115
func (c *applicationOperator) describeApplication(cluster *pb.Cluster) (*Application, error) {
H
hongming 已提交
116 117
	var app Application
	app.Name = cluster.Name.Value
H
hongming 已提交
118 119
	app.Cluster = convertCluster(cluster)
	versionInfo, err := c.opClient.DescribeAppVersions(openpitrix.SystemContext(), &pb.DescribeAppVersionsRequest{VersionId: []string{cluster.GetVersionId().GetValue()}})
H
hongming 已提交
120 121 122 123 124
	if err != nil {
		klog.Errorln(err)
		return nil, err
	}
	if len(versionInfo.AppVersionSet) > 0 {
H
hongming 已提交
125
		app.Version = convertAppVersion(versionInfo.AppVersionSet[0])
H
hongming 已提交
126
	}
H
hongming 已提交
127
	appInfo, err := c.opClient.DescribeApps(openpitrix.SystemContext(), &pb.DescribeAppsRequest{AppId: []string{cluster.GetAppId().GetValue()}, Limit: 1})
128
	if err != nil {
H
hongming 已提交
129
		klog.Errorln(err)
130 131
		return nil, err
	}
H
hongming 已提交
132
	if len(appInfo.AppSet) > 0 {
H
hongming 已提交
133
		app.App = convertApp(appInfo.GetAppSet()[0])
H
hongming 已提交
134 135 136 137
	}
	return &app, nil
}

H
hongming 已提交
138
func (c *applicationOperator) DescribeApplication(namespace string, clusterId string) (*Application, error) {
Z
zryfish 已提交
139

Z
zryfish 已提交
140
	clusters, err := c.opClient.DescribeClusters(openpitrix.SystemContext(), &pb.DescribeClustersRequest{ClusterId: []string{clusterId}, Limit: 1})
Z
zryfish 已提交
141 142

	if err != nil {
H
hongming 已提交
143
		klog.Errorln(err)
H
hongming 已提交
144
		return nil, err
Z
zryfish 已提交
145 146
	}

H
hongming 已提交
147 148 149 150 151 152 153 154
	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
	}
Z
zryfish 已提交
155
	app, err := c.describeApplication(cluster)
H
hongming 已提交
156 157 158 159 160
	if err != nil {
		klog.Errorln(err)
		return nil, err
	}

Z
zryfish 已提交
161
	workloads, err := c.getWorkLoads(namespace, cluster.ClusterRoleSet)
H
hongming 已提交
162 163 164 165 166 167

	if err != nil {
		klog.Errorln(err)
		return nil, err
	}
	app.WorkLoads = workloads
Z
zryfish 已提交
168 169 170
	workloadLabels := c.getLabels(namespace, app.WorkLoads)
	app.Services = c.getSvcs(namespace, workloadLabels)
	app.Ingresses = c.getIng(namespace, app.Services)
H
hongming 已提交
171
	return app, nil
Z
zryfish 已提交
172 173
}

Z
zryfish 已提交
174
func (c *applicationOperator) getWorkLoads(namespace string, clusterRoles []*pb.ClusterRole) (*workLoads, error) {
Z
zryfish 已提交
175 176 177

	var works workLoads
	for _, clusterRole := range clusterRoles {
H
hongming 已提交
178
		workLoadName := clusterRole.Role.Value
Z
zryfish 已提交
179
		if len(workLoadName) > 0 {
H
hongming 已提交
180 181
			if strings.HasSuffix(workLoadName, openpitrix.DeploySuffix) {
				name := strings.Split(workLoadName, openpitrix.DeploySuffix)[0]
Z
zryfish 已提交
182

Z
zryfish 已提交
183
				item, err := c.informers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(name)
Z
zryfish 已提交
184 185

				if err != nil {
186 187 188 189
					// app not ready
					if errors.IsNotFound(err) {
						continue
					}
H
hongming 已提交
190
					klog.Errorln(err)
Z
zryfish 已提交
191 192 193 194 195 196 197
					return nil, err
				}

				works.Deployments = append(works.Deployments, *item)
				continue
			}

H
hongming 已提交
198 199
			if strings.HasSuffix(workLoadName, openpitrix.DaemonSuffix) {
				name := strings.Split(workLoadName, openpitrix.DaemonSuffix)[0]
Z
zryfish 已提交
200
				item, err := c.informers.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(name)
Z
zryfish 已提交
201
				if err != nil {
202 203 204 205
					// app not ready
					if errors.IsNotFound(err) {
						continue
					}
H
hongming 已提交
206
					klog.Errorln(err)
Z
zryfish 已提交
207 208 209 210 211 212
					return nil, err
				}
				works.Daemonsets = append(works.Daemonsets, *item)
				continue
			}

H
hongming 已提交
213 214
			if strings.HasSuffix(workLoadName, openpitrix.StateSuffix) {
				name := strings.Split(workLoadName, openpitrix.StateSuffix)[0]
Z
zryfish 已提交
215
				item, err := c.informers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(name)
Z
zryfish 已提交
216
				if err != nil {
217 218 219 220
					// app not ready
					if errors.IsNotFound(err) {
						continue
					}
H
hongming 已提交
221
					klog.Errorln(err)
Z
zryfish 已提交
222 223 224 225 226 227 228 229 230 231
					return nil, err
				}
				works.Statefulsets = append(works.Statefulsets, *item)
				continue
			}
		}
	}
	return &works, nil
}

Z
zryfish 已提交
232
func (c *applicationOperator) getLabels(namespace string, workloads *workLoads) *[]map[string]string {
Z
zryfish 已提交
233

H
hongming 已提交
234
	var workloadLabels []map[string]string
Z
zryfish 已提交
235 236 237 238 239
	if workloads == nil {
		return nil
	}

	for _, workload := range workloads.Deployments {
Z
zryfish 已提交
240
		deploy, err := c.informers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(workload.Name)
Z
zryfish 已提交
241 242 243
		if errors.IsNotFound(err) {
			continue
		}
H
hongming 已提交
244
		workloadLabels = append(workloadLabels, deploy.Labels)
Z
zryfish 已提交
245 246 247
	}

	for _, workload := range workloads.Daemonsets {
Z
zryfish 已提交
248
		daemonset, err := c.informers.Apps().V1().DaemonSets().Lister().DaemonSets(namespace).Get(workload.Name)
Z
zryfish 已提交
249 250 251
		if errors.IsNotFound(err) {
			continue
		}
H
hongming 已提交
252
		workloadLabels = append(workloadLabels, daemonset.Labels)
Z
zryfish 已提交
253 254 255
	}

	for _, workload := range workloads.Statefulsets {
Z
zryfish 已提交
256
		statefulset, err := c.informers.Apps().V1().StatefulSets().Lister().StatefulSets(namespace).Get(workload.Name)
Z
zryfish 已提交
257 258 259
		if errors.IsNotFound(err) {
			continue
		}
H
hongming 已提交
260
		workloadLabels = append(workloadLabels, statefulset.Labels)
Z
zryfish 已提交
261 262
	}

H
hongming 已提交
263
	return &workloadLabels
Z
zryfish 已提交
264 265
}

H
hongming 已提交
266
func (c *applicationOperator) isExist(svcs []v1.Service, svc *v1.Service) bool {
Z
zryfish 已提交
267 268 269 270 271 272 273 274
	for _, item := range svcs {
		if item.Name == svc.Name && item.Namespace == svc.Namespace {
			return true
		}
	}
	return false
}

Z
zryfish 已提交
275
func (c *applicationOperator) getSvcs(namespace string, workLoadLabels *[]map[string]string) []v1.Service {
Z
zryfish 已提交
276 277 278 279 280
	if len(*workLoadLabels) == 0 {
		return nil
	}
	var services []v1.Service
	for _, label := range *workLoadLabels {
H
hongming 已提交
281 282
		labelSelector := labels.Set(label).AsSelector()
		svcs, err := c.informers.Core().V1().Services().Lister().Services(namespace).List(labelSelector)
Z
zryfish 已提交
283
		if err != nil {
J
Jeff 已提交
284
			klog.Errorf("get app's svc failed, reason: %v", err)
Z
zryfish 已提交
285
		}
H
hongming 已提交
286
		for _, item := range svcs {
Z
zryfish 已提交
287
			if !c.isExist(services, item) {
H
hongming 已提交
288
				services = append(services, *item)
Z
zryfish 已提交
289 290 291 292 293 294 295
			}
		}
	}

	return services
}

Z
zryfish 已提交
296
func (c *applicationOperator) getIng(namespace string, services []v1.Service) []v1beta1.Ingress {
Z
zryfish 已提交
297 298 299 300 301 302
	if services == nil {
		return nil
	}

	var ings []v1beta1.Ingress
	for _, svc := range services {
Z
zryfish 已提交
303
		ingresses, err := c.informers.Extensions().V1beta1().Ingresses().Lister().Ingresses(namespace).List(labels.Everything())
Z
zryfish 已提交
304
		if err != nil {
Z
zryfish 已提交
305 306
			klog.Error(err)
			return ings
Z
zryfish 已提交
307 308
		}

Z
zryfish 已提交
309 310 311 312
		for _, ingress := range ingresses {
			if ingress.Spec.Backend.ServiceName != svc.Name {
				continue
			}
Z
zryfish 已提交
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335

			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
}
H
hongming 已提交
336

H
hongming 已提交
337
func (c *applicationOperator) CreateApplication(namespace string, request CreateClusterRequest) error {
Z
zryfish 已提交
338
	ns, err := c.informers.Core().V1().Namespaces().Lister().Get(namespace)
H
hongming 已提交
339
	if err != nil {
H
hongming 已提交
340
		klog.Error(err)
H
hongming 已提交
341 342 343 344
		return err
	}

	if runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" {
H
hongming 已提交
345
		request.RuntimeId = runtimeId
H
hongming 已提交
346 347 348
	} else {
		return fmt.Errorf("runtime not init: namespace %s", namespace)
	}
H
hongming 已提交
349

H
hongming 已提交
350
	_, err = c.opClient.CreateCluster(openpitrix.ContextWithUsername(request.Username), &pb.CreateClusterRequest{
H
hongming 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364
		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
}

H
hongming 已提交
365
func (c *applicationOperator) ModifyApplication(request ModifyClusterAttributesRequest) error {
H
hongming 已提交
366 367 368 369 370 371 372 373 374

	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}
	}

H
hongming 已提交
375
	_, err := c.opClient.ModifyClusterAttributes(openpitrix.SystemContext(), modifyClusterAttributesRequest)
H
hongming 已提交
376 377 378 379 380

	if err != nil {
		klog.Errorln(err)
		return err
	}
H
hongming 已提交
381

H
hongming 已提交
382
	return nil
H
hongming 已提交
383 384
}

H
hongming 已提交
385
func (c *applicationOperator) DeleteApplication(clusterId string) error {
Z
zryfish 已提交
386
	_, err := c.opClient.DeleteClusters(openpitrix.SystemContext(), &pb.DeleteClustersRequest{ClusterId: []string{clusterId}, Force: &wrappers.BoolValue{Value: true}})
H
hongming 已提交
387

388
	if err != nil {
H
hongming 已提交
389
		klog.Errorln(err)
390 391 392
		return err
	}

H
hongming 已提交
393
	return nil
H
hongming 已提交
394
}