devlopment branch (#1736)

This commit is contained in:
zryfish
2020-01-02 20:52:00 +08:00
committed by GitHub
parent ff0ffe8650
commit eceadec69c
440 changed files with 61524 additions and 3699 deletions

View File

@@ -0,0 +1,131 @@
/*
Copyright 2019 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 deployment
import (
"k8s.io/client-go/informers"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"strings"
"time"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/api/apps/v1"
)
const (
applicationLabel = "app.kubernetes.io/name"
ReleaseLabel = "relase"
statusStopped = "stopped"
statusRunning = "running"
statusUpdating = "updating"
)
type deploymentsGetter struct {
sharedInformers informers.SharedInformerFactory
}
func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface {
return &deploymentsGetter{sharedInformers: sharedInformers}
}
func (d *deploymentsGetter) Get(namespace, name string) (interface{}, error) {
return d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(name)
}
func (d *deploymentsGetter) List(namespace string) ([]interface{}, error) {
// first retrieves all deployments within given namespace
all, err := d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
var result []interface{}
for _, deploy := range all {
result = append(result, deploy)
}
return result, nil
}
func (d *deploymentsGetter) Compare(left interface{}, right interface{}, field query.Field) bool {
leftDeployment, ok := left.(*v1.Deployment)
if !ok {
return false
}
rightDeployment, ok := right.(*v1.Deployment)
if !ok {
return false
}
switch field {
case query.FieldCreationTimeStamp:
return leftDeployment.CreationTimestamp.After(rightDeployment.CreationTimestamp.Time)
case query.FieldLastUpdateTimestamp:
return lastUpdateTime(leftDeployment).After(lastUpdateTime(rightDeployment))
default:
fallthrough
case query.FieldName:
return strings.Compare(leftDeployment.Name, rightDeployment.Name) > 0
}
}
func (d *deploymentsGetter) Filter(object interface{}, filter query.Filter) bool {
deployment, ok := object.(*v1.Deployment)
if !ok {
return false
}
switch filter.Field {
case query.FieldName:
return query.ComparableString(deployment.Name).Contains(filter.Value)
case query.FieldApplication:
if app, ok := deployment.Labels[applicationLabel]; ok {
return query.ComparableString(app).Contains(filter.Value)
}
case query.FieldStatus:
return filter.Value.Compare(query.ComparableString(deploymentStatus(deployment.Status))) == 0
default:
return false
}
return false
}
func deploymentStatus(status v1.DeploymentStatus) string {
if status.ReadyReplicas == 0 && status.Replicas == 0 {
return statusStopped
} else if status.ReadyReplicas == status.Replicas {
return statusRunning
} else {
return statusUpdating
}
}
func lastUpdateTime(deployment *v1.Deployment) time.Time {
lut := deployment.CreationTimestamp.Time
for _, condition := range deployment.Status.Conditions {
if condition.LastUpdateTime.After(lut) {
lut = condition.LastUpdateTime.Time
}
}
return lut
}

View File

@@ -0,0 +1,150 @@
package deployment
import (
"fmt"
"github.com/google/go-cmp/cmp"
v1 "k8s.io/api/apps/v1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"testing"
"time"
)
func newDeployments(total int, name, namespace, application string) []*v1.Deployment {
var deployments []*v1.Deployment
for i := 0; i < total; i++ {
deploy := &v1.Deployment{
TypeMeta: metaV1.TypeMeta{
Kind: "Deployment",
APIVersion: "v1",
},
ObjectMeta: metaV1.ObjectMeta{
Name: fmt.Sprintf("%s-%d", name, i),
Namespace: namespace,
Labels: map[string]string{
"seq": fmt.Sprintf("seq-%d", i),
},
Annotations: map[string]string{},
CreationTimestamp: metaV1.Time{Time: time.Now().Add(time.Duration(i*5) * time.Second)},
},
Status: v1.DeploymentStatus{
ReadyReplicas: int32(i + 1),
Replicas: int32(i + 1),
AvailableReplicas: int32(i + 1),
Conditions: []v1.DeploymentCondition{
{
Type: v1.DeploymentAvailable,
LastUpdateTime: metaV1.Time{Time: time.Now().Add(time.Duration(i*5) * time.Second)},
},
},
},
}
deployments = append(deployments, deploy)
}
return deployments
}
func deploymentsToRuntimeObjects(deployments ...*v1.Deployment) []runtime.Object {
var objs []runtime.Object
for _, deploy := range deployments {
objs = append(objs, deploy)
}
return objs
}
func TestListDeployments(t *testing.T) {
tests := []struct {
description string
namespace string
deployments []*v1.Deployment
query *query.Query
expected api.ListResult
expectedErr error
}{
{
"test name filter",
"bar",
[]*v1.Deployment{
{
ObjectMeta: metaV1.ObjectMeta{
Name: "foo-1",
Namespace: "bar",
},
},
{
ObjectMeta: metaV1.ObjectMeta{
Name: "foo-2",
Namespace: "bar",
},
},
{
ObjectMeta: metaV1.ObjectMeta{
Name: "bar-1",
Namespace: "bar",
},
},
},
&query.Query{
Pagination: &query.Pagination{
Limit: 1,
Page: 1,
},
SortBy: query.FieldName,
Ascending: false,
Filters: []query.Filter{
{
Field: query.FieldName,
Value: query.ComparableString("foo"),
},
},
},
api.ListResult{
Items: []interface{}{
v1.Deployment{
ObjectMeta: metaV1.ObjectMeta{
Name: "foo-2",
Namespace: "bar",
},
},
},
TotalItems: 2,
},
nil,
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
objs := deploymentsToRuntimeObjects(test.deployments...)
client := fake.NewSimpleClientset(objs...)
//client := fake.NewSimpleClientset()
informer := informers.NewSharedInformerFactory(client, 0)
for _, deployment := range test.deployments {
informer.Apps().V1().Deployments().Informer().GetIndexer().Add(deployment)
}
getter := New(informer)
got, err := getter.List(test.namespace, test.query)
if test.expectedErr != nil && err != test.expectedErr {
t.Errorf("expected error, got nothing")
} else if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(got, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
}
})
}
}

View File

@@ -0,0 +1,19 @@
package v1alpha3
import (
"kubesphere.io/kubesphere/pkg/apiserver/query"
)
type Interface interface {
// Get retrieves a single object by its namespace and name
Get(namespace, name string) (interface{}, error)
// List retrieves a collection of objects matches given query
List(namespace string) ([]interface{}, error)
//
Filter(item interface{}, filter query.Filter) bool
//
Compare(left interface{}, right interface{}, field query.Field) bool
}

View File

@@ -0,0 +1,98 @@
package resource
import (
"errors"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/informers"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/deployment"
"sort"
)
var ErrResourceNotSupported = errors.New("resource is not supported")
type NamespacedResourceGetter struct {
getters map[schema.GroupVersionResource]v1alpha3.Interface
}
func New(informers informers.SharedInformerFactory) *NamespacedResourceGetter {
getters := make(map[schema.GroupVersionResource]v1alpha3.Interface)
getters[schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"}] = deployment.New(informers)
return &NamespacedResourceGetter{
getters: getters,
}
}
// tryResource will retrieve a getter with resource name, it doesn't guarantee find resource with correct group version
// need to refactor this use schema.GroupVersionResource
func (r *NamespacedResourceGetter) tryResource(resource string) v1alpha3.Interface {
for k, v := range r.getters {
if k.Resource == resource {
return v
}
}
return nil
}
func (r *NamespacedResourceGetter) Get(resource, namespace, name string) (interface{}, error) {
getter := r.tryResource(resource)
if getter == nil {
return nil, ErrResourceNotSupported
}
return getter.Get(namespace, name)
}
func (r *NamespacedResourceGetter) List(resource, namespace string, query *query.Query) (*api.ListResult, error) {
getter := r.tryResource(resource)
if getter == nil {
return nil, ErrResourceNotSupported
}
all, err := getter.List(namespace)
if err != nil {
return nil, err
}
// selected matched ones
var filtered []interface{}
for _, deploy := range all {
for _, filter := range query.Filters {
if getter.Filter(deploy, filter) {
filtered = append(filtered, deploy)
}
}
}
// sort
sort.Slice(filtered, func(i, j int) bool {
if !query.Ascending {
return !getter.Compare(filtered[i], filtered[j], query.SortBy)
}
return getter.Compare(filtered[i], filtered[j], query.SortBy)
})
start, end := query.Pagination.GetPaginationSettings(len(filtered))
if query.Pagination.IsPageAvailable(len(filtered), start) {
var result []interface{}
for i := start; i < end; i++ {
result = append(result, filtered[i])
}
return &api.ListResult{
Items: result,
TotalItems: len(filtered),
}, nil
}
return &api.ListResult{
Items: nil,
TotalItems: len(filtered),
}, nil
}