提交 4ae7d325 编写于 作者: H hongming 提交者: zryfish

fix: application api

Signed-off-by: Nhongming <talonwan@yunify.com>
上级 4c111533
......@@ -23,7 +23,6 @@ import (
// Install apis
_ "kubesphere.io/kubesphere/pkg/apis/devops/install"
_ "kubesphere.io/kubesphere/pkg/apis/logging/install"
_ "kubesphere.io/kubesphere/pkg/apis/metrics/install"
_ "kubesphere.io/kubesphere/pkg/apis/monitoring/install"
_ "kubesphere.io/kubesphere/pkg/apis/operations/install"
_ "kubesphere.io/kubesphere/pkg/apis/resources/install"
......
/*
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 install
import (
"github.com/emicklei/go-restful"
urlruntime "k8s.io/apimachinery/pkg/util/runtime"
metrcisv1alpha2 "kubesphere.io/kubesphere/pkg/apis/metrics/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
func init() {
Install(runtime.Container)
}
func Install(container *restful.Container) {
urlruntime.Must(metrcisv1alpha2.AddToContainer(container))
}
/*
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 v1alpha2
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful-openapi"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/apiserver/metrics"
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
)
const GroupName = "metrics.kubesphere.io"
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var (
WebServiceBuilder = runtime.NewContainerBuilder(addWebService)
AddToContainer = WebServiceBuilder.AddToContainer
)
func addWebService(c *restful.Container) error {
webservice := runtime.NewWebService(GroupVersion)
tags := []string{"metrics"}
webservice.Route(webservice.GET("/storageclasses/{storageclass}").
To(metrics.GetScMetrics).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("").
Param(webservice.PathParameter("storageclass", "storageclass's name")).
Writes(metrics.ScMetricsItem{}))
webservice.Route(webservice.GET("/storageclasses").
To(metrics.GetScMetricsList).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("").
Writes([]metrics.ScMetricsItem{}))
c.Add(webservice)
return nil
}
......@@ -34,8 +34,10 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/workloadstatuses"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/applications"
gitmodel "kubesphere.io/kubesphere/pkg/models/git"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
)
const GroupName = "resources.kubesphere.io"
......@@ -88,7 +90,7 @@ func addWebService(c *restful.Container) error {
tags = []string{"Applications"}
webservice.Route(webservice.GET("/applications").
To(resources.ApplicationHandler).
To(resources.ListApplication).
Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("List applications in cluster").
......@@ -104,7 +106,7 @@ func addWebService(c *restful.Container) error {
DefaultValue("limit=10,page=1")))
webservice.Route(webservice.GET("/namespaces/{namespace}/applications").
To(resources.NamespacedApplicationHandler).
To(resources.ListNamespacedApplication).
Writes(models.PageableResponse{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("List applications").
......@@ -118,11 +120,26 @@ func addWebService(c *restful.Container) error {
DataFormat("limit=%d,page=%d").
DefaultValue("limit=10,page=1")))
webservice.Route(webservice.GET("/storageclasses/{storageclass}/persistentvolumeclaims").
To(resources.GetPvcListBySc).
Doc("query persistent volume claims by storageclass").
Param(webservice.PathParameter("username", "username")).
Metadata(restfulspec.KeyOpenAPITags, tags))
webservice.Route(webservice.GET("/namespaces/{namespace}/applications/{cluster_id}").
To(resources.DescribeApplication).
Writes(applications.Application{}).
Metadata(restfulspec.KeyOpenAPITags, tags).
Doc("Describe application").
Param(webservice.PathParameter("namespace", "namespace name")).
Param(webservice.PathParameter("cluster_id", "openpitrix cluster id")))
webservice.Route(webservice.POST("/namespaces/{namespace}/applications").
To(resources.DeployApplication).
Doc("Deploy application").
Metadata(restfulspec.KeyOpenAPITags, tags).
Reads(openpitrix.CreateClusterRequest{}).
Param(webservice.PathParameter("namespace", "namespace name")))
webservice.Route(webservice.DELETE("/namespaces/{namespace}/applications/{cluster_id}").
To(resources.DeleteApplication).
Doc("Delete application").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(webservice.PathParameter("namespace", "namespace name")))
tags = []string{"User resources"}
......
/*
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 metrics
import (
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/errors"
"kubesphere.io/kubesphere/pkg/models/storage"
"net/http"
)
type ScMetricsItem struct {
Name string `json:"name"`
Metrics *storage.ScMetrics `json:"metrics"`
}
// Get StorageClass item
// Extended API URL: "GET /api/v1alpha1/storage/storageclasses/{storageclass}/metrics"
func GetScMetrics(request *restful.Request, response *restful.Response) {
scName := request.PathParameter("storageclass")
metrics, err := storage.GetScMetrics(scName)
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
result := ScMetricsItem{
Name: scName, Metrics: metrics,
}
response.WriteAsJson(result)
}
// Get StorageClass item list
// Extended API URL: "GET /api/v1alpha1/storage/storageclasses/metrics"
func GetScMetricsList(request *restful.Request, response *restful.Response) {
scList, err := storage.GetScList()
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
// Set return value
items := make([]ScMetricsItem, 0)
for _, v := range scList {
metrics, err := storage.GetScMetrics(v.GetName())
if err != nil {
response.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
item := ScMetricsItem{
Name: v.GetName(), Metrics: metrics,
}
items = append(items, item)
}
response.WriteAsJson(items)
}
......@@ -18,6 +18,7 @@
package resources
import (
"fmt"
"github.com/emicklei/go-restful"
"github.com/golang/glog"
"k8s.io/api/core/v1"
......@@ -27,10 +28,11 @@ import (
"kubesphere.io/kubesphere/pkg/models/applications"
"kubesphere.io/kubesphere/pkg/models/resources"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"net/http"
)
func ApplicationHandler(req *restful.Request, resp *restful.Response) {
func ListApplication(req *restful.Request, resp *restful.Response) {
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
clusterId := req.QueryParameter("cluster_id")
runtimeId := req.QueryParameter("runtime_id")
......@@ -63,7 +65,7 @@ func ApplicationHandler(req *restful.Request, resp *restful.Response) {
}
func NamespacedApplicationHandler(req *restful.Request, resp *restful.Response) {
func ListNamespacedApplication(req *restful.Request, resp *restful.Response) {
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
namespaceName := req.PathParameter("namespace")
clusterId := req.QueryParameter("cluster_id")
......@@ -113,3 +115,92 @@ func NamespacedApplicationHandler(req *restful.Request, resp *restful.Response)
resp.WriteAsJson(result)
}
func DescribeApplication(req *restful.Request, resp *restful.Response) {
clusterId := req.PathParameter("cluster_id")
namespaceName := req.PathParameter("namespace")
app, err := applications.GetApp(clusterId)
if err != nil {
glog.Errorln("get app failed", err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
namespace, err := resources.GetResource("", resources.Namespaces, namespaceName)
if err != nil {
glog.Errorln("get namespace failed", err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
var runtimeId string
if ns, ok := namespace.(*v1.Namespace); ok {
runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey]
}
if runtimeId != app.RuntimeId {
glog.Errorln("runtime not match", app.RuntimeId, runtimeId)
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.New(fmt.Sprintf("rumtime not match %s,%s", app.RuntimeId, runtimeId)))
return
}
resp.WriteEntity(app)
return
}
func DeployApplication(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
var app openpitrix.CreateClusterRequest
err := req.ReadEntity(&app)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
err = applications.DeployApplication(namespace, app)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteEntity(errors.None)
}
func DeleteApplication(req *restful.Request, resp *restful.Response) {
clusterId := req.PathParameter("cluster_id")
namespaceName := req.PathParameter("namespace")
app, err := applications.GetApp(clusterId)
if err != nil {
glog.Errorln("get app failed", err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
namespace, err := resources.GetResource("", resources.Namespaces, namespaceName)
if err != nil {
glog.Errorln("get namespace failed", err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
var runtimeId string
if ns, ok := namespace.(*v1.Namespace); ok {
runtimeId = ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey]
}
if runtimeId != app.RuntimeId {
glog.Errorln("runtime not match", app.RuntimeId, runtimeId)
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.New(fmt.Sprintf("rumtime not match %s,%s", app.RuntimeId, runtimeId)))
return
}
err = applications.DeleteApplication(clusterId)
if err != nil {
glog.Errorln("delete application failed", err)
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteEntity(errors.None)
}
......@@ -18,6 +18,7 @@
package applications
import (
"fmt"
"github.com/golang/glog"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
......@@ -25,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/resources"
......@@ -283,3 +285,22 @@ func getIng(namespace string, services []v1.Service) []v1beta1.Ingress {
return ings
}
func DeployApplication(namespace string, app openpitrix.CreateClusterRequest) error {
ns, err := informers.SharedInformerFactory().Core().V1().Namespaces().Lister().Get(namespace)
if err != nil {
glog.Errorf("deploy application failed: %+v", err)
return err
}
if runtimeId := ns.Annotations[constants.OpenPitrixRuntimeAnnotationKey]; runtimeId != "" {
app.RuntimeId = runtimeId
} else {
return fmt.Errorf("runtime not init: namespace %s", namespace)
}
return openpitrix.CreateCluster(app)
}
func DeleteApplication(clusterId string) error {
return openpitrix.DeleteCluster(openpitrix.DeleteClusterRequest{ClusterId: []string{clusterId}})
}
......@@ -358,18 +358,9 @@ var (
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"list"},
APIGroups: []string{"openpitrix.io"},
Resources: []string{"repos", "app_versions"},
}, {
Verbs: []string{"get"},
APIGroups: []string{"openpitrix.io"},
Resources: []string{"app_version/*"},
},
{
Verbs: []string{"*"},
Verbs: []string{"get", "list"},
APIGroups: []string{"openpitrix.io"},
Resources: []string{"apps", "clusters"},
Resources: []string{"apps", "clusters", "repos", "app_versions", "app_version/*"},
},
},
},
......@@ -916,6 +907,10 @@ var (
Verbs: []string{"get", "list"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"applications"},
}, {
Verbs: []string{"get", "list"},
APIGroups: []string{"servicemesh.kubesphere.io"},
Resources: []string{"*"},
},
{
Verbs: []string{"list"},
......@@ -931,19 +926,27 @@ var (
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"update", "patch"},
APIGroups: []string{"openpitrix.io"},
Resources: []string{"apps"},
Verbs: []string{"create", "update", "patch"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"applications"},
}, {
Verbs: []string{"create", "update", "patch"},
APIGroups: []string{"servicemesh.kubesphere.io"},
Resources: []string{"*"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"openpitrix.io"},
ResourceNames: []string{"delete"},
Resources: []string{"clusters"},
Verbs: []string{"delete"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"applications"},
},
{
Verbs: []string{"delete"},
APIGroups: []string{"servicemesh.kubesphere.io"},
Resources: []string{"*"},
},
},
},
......@@ -954,7 +957,7 @@ var (
Actions: []models.Action{
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
Verbs: []string{"get", "list"},
APIGroups: []string{"batch", "resources.kubesphere.io"},
Resources: []string{"jobs"},
},
......@@ -987,7 +990,7 @@ var (
Actions: []models.Action{
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
Verbs: []string{"get", "list"},
APIGroups: []string{"batch", "resources.kubesphere.io"},
Resources: []string{"cronjobs"},
},
......@@ -1020,7 +1023,7 @@ var (
Actions: []models.Action{
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
Verbs: []string{"get", "list"},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"secrets"},
},
......@@ -1053,7 +1056,7 @@ var (
Actions: []models.Action{
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
Verbs: []string{"get", "list"},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"configmaps"},
},
......
......@@ -19,9 +19,11 @@ package resources
import (
"github.com/golang/glog"
"k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1"
"k8s.io/apimachinery/pkg/labels"
"kubesphere.io/kubesphere/pkg/informers"
"strconv"
)
type extraAnnotationInjector struct {
......@@ -30,14 +32,36 @@ type extraAnnotationInjector struct {
func (i extraAnnotationInjector) addExtraAnnotations(item interface{}) interface{} {
switch item.(type) {
case *v1.PersistentVolumeClaim:
return i.injectPersistentVolumeClaim(item.(*v1.PersistentVolumeClaim))
case *corev1.PersistentVolumeClaim:
return i.injectPersistentVolumeClaim(item.(*corev1.PersistentVolumeClaim))
case *storagev1.StorageClass:
return i.injectStorageClass(item.(*storagev1.StorageClass))
}
return item
}
func (i extraAnnotationInjector) injectPersistentVolumeClaim(item *v1.PersistentVolumeClaim) *v1.PersistentVolumeClaim {
func (i extraAnnotationInjector) injectStorageClass(item *storagev1.StorageClass) *storagev1.StorageClass {
count, err := countPvcByStorageClass(item.Name)
if err != nil {
glog.Errorf("inject annotation failed %+v", err)
return item
}
item = item.DeepCopy()
if item.Annotations == nil {
item.Annotations = make(map[string]string, 0)
}
item.Annotations["kubesphere.io/pvc-count"] = strconv.Itoa(count)
return item
}
func (i extraAnnotationInjector) injectPersistentVolumeClaim(item *corev1.PersistentVolumeClaim) *corev1.PersistentVolumeClaim {
podLister := informers.SharedInformerFactory().Core().V1().Pods().Lister()
pods, err := podLister.Pods(item.Namespace).List(labels.Everything())
if err != nil {
......@@ -60,7 +84,7 @@ func (i extraAnnotationInjector) injectPersistentVolumeClaim(item *v1.Persistent
return item
}
func isPvcInUse(pods []*v1.Pod, pvcName string) bool {
func isPvcInUse(pods []*corev1.Pod, pvcName string) bool {
for _, pod := range pods {
volumes := pod.Spec.Volumes
for _, volume := range volumes {
......@@ -72,3 +96,25 @@ func isPvcInUse(pods []*v1.Pod, pvcName string) bool {
}
return false
}
func countPvcByStorageClass(scName string) (int, error) {
persistentVolumeClaimLister := informers.SharedInformerFactory().Core().V1().PersistentVolumeClaims().Lister()
all, err := persistentVolumeClaimLister.List(labels.Everything())
if err != nil {
return 0, err
}
count := 0
for _, item := range all {
if item.Spec.StorageClassName != nil {
if *item.Spec.StorageClassName == scName {
count++
}
} else if item.GetAnnotations()[corev1.BetaStorageClassAnnotation] == scName {
count++
}
}
return count, nil
}
......@@ -101,6 +101,17 @@ type repoList struct {
Repos []repo `json:"repo_set"`
}
type CreateClusterRequest struct {
AppId string `json:"app_id"`
VersionId string `json:"version_id"`
RuntimeId string `json:"runtime_id"`
Conf string `json:"conf"`
}
type DeleteClusterRequest struct {
ClusterId []string `json:"cluster_id"`
}
func GetAppInfo(appId string) (string, string, string, error) {
url := fmt.Sprintf("%s/v1/apps?app_id=%s", openpitrixAPIServer, appId)
resp, err := makeHttpRequest("GET", url, "")
......@@ -256,6 +267,48 @@ func GetRuntime(runtimeId string) (string, error) {
return runtimes.Runtimes[0].Zone, nil
}
func CreateCluster(request CreateClusterRequest) error {
versionUrl := fmt.Sprintf("%s/v1/clusters/create", openpitrixAPIServer)
data, err := json.Marshal(request)
if err != nil {
glog.Error(err)
return err
}
data, err = makeHttpRequest("POST", versionUrl, string(data))
if err != nil {
glog.Error(err)
return err
}
return nil
}
func DeleteCluster(request DeleteClusterRequest) error {
versionUrl := fmt.Sprintf("%s/v1/clusters/delete", openpitrixAPIServer)
data, err := json.Marshal(request)
if err != nil {
glog.Error(err)
return err
}
data, err = makeHttpRequest("POST", versionUrl, string(data))
if err != nil {
glog.Error(err)
return err
}
return nil
}
func makeHttpRequest(method, url, data string) ([]byte, error) {
var req *http.Request
......
......@@ -32,7 +32,6 @@ import (
"log"
// Install apis
_ "kubesphere.io/kubesphere/pkg/apis/devops/install"
_ "kubesphere.io/kubesphere/pkg/apis/metrics/install"
_ "kubesphere.io/kubesphere/pkg/apis/monitoring/install"
_ "kubesphere.io/kubesphere/pkg/apis/operations/install"
_ "kubesphere.io/kubesphere/pkg/apis/resources/install"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册