mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
Implement component related API
Update app list/show, implmented component show
This commit is contained in:
@@ -150,7 +150,7 @@ $ vela route mycomp --app myapp
|
||||
It will create route trait for this component.
|
||||
|
||||
```shell script
|
||||
$ kubeclt get routes.standard.oam.dev -n myenv
|
||||
$ kubectl get routes.standard.oam.dev -n myenv
|
||||
NAME AGE
|
||||
mycomp-trait-5b576c4fc 18s
|
||||
```
|
||||
|
||||
@@ -12,34 +12,20 @@ import (
|
||||
"github.com/oam-dev/kubevela/cmd/vela/fake"
|
||||
"github.com/oam-dev/kubevela/pkg/commands"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/system"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
|
||||
"github.com/crossplane/oam-kubernetes-runtime/apis/core"
|
||||
"github.com/gosuri/uitable"
|
||||
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
|
||||
"github.com/spf13/cobra"
|
||||
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/klog"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = k8sruntime.NewScheme()
|
||||
)
|
||||
|
||||
// chartTGZSource is a base64-encoded, gzipped tarball of the default Helm chart.
|
||||
// Its value is initialized at build time.
|
||||
var chartTGZSource string
|
||||
|
||||
func init() {
|
||||
_ = clientgoscheme.AddToScheme(scheme)
|
||||
_ = certmanager.AddToScheme(scheme)
|
||||
_ = core.AddToScheme(scheme)
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
@@ -83,7 +69,7 @@ func newCommand() *cobra.Command {
|
||||
|
||||
commandArgs := types.Args{
|
||||
Config: restConf,
|
||||
Schema: scheme,
|
||||
Schema: oam.Scheme,
|
||||
}
|
||||
|
||||
if err := system.InitDirs(); err != nil {
|
||||
|
||||
@@ -171,44 +171,93 @@ sample response
|
||||
sample response
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"Status": "UNKNOWN",
|
||||
"Workload": {
|
||||
"workload": {
|
||||
"apiVersion": "core.oam.dev/v1alpha2",
|
||||
"kind": "ContainerizedWorkload",
|
||||
"metadata": {
|
||||
"name": "poc5"
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"image": "nginx:1.9.4",
|
||||
"name": "poc5",
|
||||
"ports": [{
|
||||
"containerPort": 80,
|
||||
"name": "default",
|
||||
"protocol": "TCP"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Traits": [{
|
||||
"trait": {
|
||||
"apiVersion": "core.oam.dev/v1alpha2",
|
||||
"kind": "ManualScalerTrait",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"vela.oam.dev/traitDef": "scale"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"replicaCount": 2
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
"code": 200,
|
||||
"data": {
|
||||
"Status": "True",
|
||||
"Components": [
|
||||
{
|
||||
"name": "web-comp",
|
||||
"Status": "True",
|
||||
"workload": {
|
||||
"apiVersion": "standard.oam.dev/v1alpha1",
|
||||
"kind": "Containerized",
|
||||
"metadata": {
|
||||
"name": "web-comp"
|
||||
},
|
||||
"spec": {
|
||||
"podSpec": {
|
||||
"containers": [
|
||||
{
|
||||
"image": "nginx:1.9.4",
|
||||
"name": "web-comp",
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 80,
|
||||
"name": "default",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"replicas": 1
|
||||
}
|
||||
},
|
||||
"Traits": [
|
||||
{
|
||||
"trait": {
|
||||
"apiVersion": "standard.oam.dev/v1alpha1",
|
||||
"kind": "Route",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"trait.oam.dev/name": "route"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"backend": {
|
||||
"port": 0
|
||||
},
|
||||
"host": "web-comp.poc.oam.dev",
|
||||
"path": "",
|
||||
"tls": {
|
||||
"issuerName": "oam-env-default"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "comp1",
|
||||
"Status": "True",
|
||||
"workload": {
|
||||
"apiVersion": "standard.oam.dev/v1alpha1",
|
||||
"kind": "Containerized",
|
||||
"metadata": {
|
||||
"name": "comp1"
|
||||
},
|
||||
"spec": {
|
||||
"podSpec": {
|
||||
"containers": [
|
||||
{
|
||||
"image": "nginx:1.9.4",
|
||||
"name": "comp1",
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 6379,
|
||||
"name": "default",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"replicas": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
### DELETE /api/envs/:envName/apps/:appName (app delete)
|
||||
@@ -221,8 +270,74 @@ sample response
|
||||
}
|
||||
```
|
||||
|
||||
## Components
|
||||
### GET /api/envs/:envName/apps/:appName/components/:compName (component details)
|
||||
- example
|
||||
sample response
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"name": "web-comp",
|
||||
"Status": "True",
|
||||
"workload": {
|
||||
"apiVersion": "standard.oam.dev/v1alpha1",
|
||||
"kind": "Containerized",
|
||||
"metadata": {
|
||||
"name": "web-comp"
|
||||
},
|
||||
"spec": {
|
||||
"podSpec": {
|
||||
"containers": [
|
||||
{
|
||||
"image": "nginx:1.9.4",
|
||||
"name": "web-comp",
|
||||
"ports": [
|
||||
{
|
||||
"containerPort": 80,
|
||||
"name": "default",
|
||||
"protocol": "TCP"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"replicas": 1
|
||||
}
|
||||
},
|
||||
"Traits": [
|
||||
{
|
||||
"trait": {
|
||||
"apiVersion": "standard.oam.dev/v1alpha1",
|
||||
"kind": "Route",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"trait.oam.dev/name": "route"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"backend": {
|
||||
"port": 0
|
||||
},
|
||||
"host": "web-comp.poc.oam.dev",
|
||||
"path": "",
|
||||
"tls": {
|
||||
"issuerName": "oam-env-default"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/envs/:envName/apps/:appName/components/ (component list)
|
||||
Same as `GET /api/envs/:envName/apps/:appName (app description)`.
|
||||
|
||||
|
||||
## Workloads
|
||||
### POST /api/workloads/ (workload create)
|
||||
### POST /api/workloads/ (workload create, component create)
|
||||
- parameters
|
||||
```go
|
||||
type WorkloadRunBody struct {
|
||||
|
||||
@@ -5,11 +5,10 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/oam-dev/kubevela/api/types"
|
||||
"github.com/oam-dev/kubevela/pkg/application"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/commands/util"
|
||||
|
||||
"github.com/gosuri/uitable"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
@@ -17,10 +17,10 @@ import (
|
||||
)
|
||||
|
||||
type ComponentMeta struct {
|
||||
Name string `json:"name"`
|
||||
App string `json:"app"`
|
||||
Workload string `json:"workload,omitempty"`
|
||||
Traits []string `json:"traits,omitempty"`
|
||||
// Name string `json:"name"`
|
||||
App string `json:"app"`
|
||||
// Workload string `json:"workload,omitempty"`
|
||||
// Traits []string `json:"traits,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
CreatedTime string `json:"created,omitempty"`
|
||||
AppConfig corev1alpha2.ApplicationConfiguration `json:"-"`
|
||||
@@ -79,17 +79,17 @@ func ListComponents(ctx context.Context, c client.Client, opt Option) ([]Compone
|
||||
if err != nil {
|
||||
return componentMetaList, err
|
||||
}
|
||||
traitAlias := GetTraitAliasByComponentTraitList(com.Traits)
|
||||
var workload string
|
||||
if component.Annotations != nil {
|
||||
workload = component.Annotations[types.AnnWorkloadDef]
|
||||
}
|
||||
//traitAlias := GetTraitAliasByComponentTraitList(com.Traits)
|
||||
//var workload string
|
||||
//if component.Annotations != nil {
|
||||
// workload = component.Annotations[types.AnnWorkloadDef]
|
||||
//}
|
||||
componentMetaList = append(componentMetaList, ComponentMeta{
|
||||
Name: com.ComponentName,
|
||||
App: a.Name,
|
||||
Workload: workload,
|
||||
Status: types.StatusDeployed,
|
||||
Traits: traitAlias,
|
||||
// Name: com.ComponentName,
|
||||
App: a.Name,
|
||||
//Workload: workload,
|
||||
Status: types.StatusDeployed,
|
||||
//Traits: traitAlias,
|
||||
CreatedTime: a.ObjectMeta.CreationTimestamp.String(),
|
||||
Component: component,
|
||||
AppConfig: a,
|
||||
@@ -100,32 +100,36 @@ func ListComponents(ctx context.Context, c client.Client, opt Option) ([]Compone
|
||||
return componentMetaList, nil
|
||||
}
|
||||
|
||||
func RetrieveApplicationStatusByName(ctx context.Context, c client.Client, applicationName string, namespace string) (apis.ApplicationStatusMeta, error) {
|
||||
var applicationStatusMeta apis.ApplicationStatusMeta
|
||||
func RetrieveApplicationStatusByName(ctx context.Context, c client.Client, applicationName string, namespace string) (apis.ApplicationMeta, error) {
|
||||
var applicationMeta apis.ApplicationMeta
|
||||
var appConfig corev1alpha2.ApplicationConfiguration
|
||||
if err := c.Get(ctx, client.ObjectKey{Name: applicationName, Namespace: namespace}, &appConfig); err != nil {
|
||||
return applicationStatusMeta, err
|
||||
return applicationMeta, err
|
||||
}
|
||||
|
||||
var status = "Unknown"
|
||||
if len(appConfig.Status.Conditions) != 0 {
|
||||
status = string(appConfig.Status.Conditions[0].Status)
|
||||
}
|
||||
applicationMeta.Status = status
|
||||
|
||||
for _, com := range appConfig.Spec.Components {
|
||||
// Just get the one component from appConfig
|
||||
if com.ComponentName != applicationName {
|
||||
continue
|
||||
}
|
||||
component, err := cmdutil.GetComponent(ctx, c, com.ComponentName, namespace)
|
||||
componentName := com.ComponentName
|
||||
component, err := cmdutil.GetComponent(ctx, c, componentName, namespace)
|
||||
if err != nil {
|
||||
return applicationStatusMeta, err
|
||||
return applicationMeta, err
|
||||
}
|
||||
var status = "UNKNOWN"
|
||||
if len(appConfig.Status.Conditions) != 0 {
|
||||
status = string(appConfig.Status.Conditions[0].Status)
|
||||
}
|
||||
applicationStatusMeta = apis.ApplicationStatusMeta{
|
||||
|
||||
applicationMeta.Components = append(applicationMeta.Components, apis.ComponentMeta{
|
||||
Name: componentName,
|
||||
Status: status,
|
||||
Workload: component.Spec,
|
||||
Workload: component.Spec.Workload,
|
||||
Traits: com.Traits,
|
||||
}
|
||||
})
|
||||
applicationMeta.Status = status
|
||||
|
||||
}
|
||||
return applicationStatusMeta, nil
|
||||
return applicationMeta, nil
|
||||
}
|
||||
|
||||
func (o *DeleteOptions) DeleteApp() (string, error) {
|
||||
|
||||
42
pkg/oam/common.go
Normal file
42
pkg/oam/common.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package oam
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/crossplane/oam-kubernetes-runtime/apis/core"
|
||||
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/api/types"
|
||||
k8sruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
var (
|
||||
Scheme = k8sruntime.NewScheme()
|
||||
)
|
||||
|
||||
func init() {
|
||||
_ = clientgoscheme.AddToScheme(Scheme)
|
||||
_ = certmanager.AddToScheme(Scheme)
|
||||
_ = core.AddToScheme(Scheme)
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
func InitKubeClient() (client.Client, error) {
|
||||
restConf, err := config.GetConfig()
|
||||
if err != nil {
|
||||
fmt.Println("get kubeConfig err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
commandArgs := types.Args{
|
||||
Config: restConf,
|
||||
Schema: Scheme,
|
||||
}
|
||||
|
||||
return client.New(commandArgs.Config, client.Options{Scheme: commandArgs.Schema})
|
||||
}
|
||||
24
pkg/oam/component.go
Normal file
24
pkg/oam/component.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package oam
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/server/apis"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func RetrieveComponent(ctx context.Context, c client.Client, applicationName, componentName, namespace string) (apis.ComponentMeta, error) {
|
||||
var componentMeta apis.ComponentMeta
|
||||
applicationMeta, err := RetrieveApplicationStatusByName(ctx, c, applicationName, namespace)
|
||||
if err != nil {
|
||||
return componentMeta, err
|
||||
}
|
||||
|
||||
for _, com := range applicationMeta.Components {
|
||||
if com.Name != componentName {
|
||||
continue
|
||||
}
|
||||
return com, nil
|
||||
}
|
||||
return componentMeta, nil
|
||||
}
|
||||
@@ -68,10 +68,16 @@ type TraitBody struct {
|
||||
Staging string `json:"staging,omitempty"`
|
||||
}
|
||||
|
||||
type ApplicationStatusMeta struct {
|
||||
Status string `json:"status,omitempty"`
|
||||
Workload corev1alpha2.ComponentSpec `json:"workload,omitempty"`
|
||||
Traits []corev1alpha2.ComponentTrait `json:"traits,omitempty"`
|
||||
type ComponentMeta struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"Status,omitempty"`
|
||||
Workload runtime.RawExtension `json:"workload,omitempty"`
|
||||
Traits []corev1alpha2.ComponentTrait `json:"Traits,omitempty"`
|
||||
}
|
||||
|
||||
type ApplicationMeta struct {
|
||||
Status string `json:"Status,omitempty"`
|
||||
Components []ComponentMeta `json:"Components,omitempty"`
|
||||
}
|
||||
|
||||
type CapabilityMeta struct {
|
||||
|
||||
28
pkg/server/handler/componentHandlers.go
Normal file
28
pkg/server/handler/componentHandlers.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/server/util"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func GetComponent(c *gin.Context) {
|
||||
kubeClient := c.MustGet("KubeClient")
|
||||
envName := c.Param("envName")
|
||||
envMeta, err := oam.GetEnvByName(envName)
|
||||
if err != nil {
|
||||
util.HandleError(c, util.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
namespace := envMeta.Namespace
|
||||
applicationName := c.Param("appName")
|
||||
componentName := c.Param("compName")
|
||||
ctx := util.GetContext(c)
|
||||
componentMeta, err := oam.RetrieveComponent(ctx, kubeClient.(client.Client), applicationName, componentName, namespace)
|
||||
if err != nil {
|
||||
util.HandleError(c, util.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
util.AssembleResponse(c, componentMeta, nil)
|
||||
}
|
||||
50
pkg/server/main/startAPIServer.go
Normal file
50
pkg/server/main/startAPIServer.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/server"
|
||||
"github.com/oam-dev/kubevela/pkg/server/util"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
)
|
||||
|
||||
// main will only start up API server
|
||||
func main() {
|
||||
var development = true
|
||||
// setup logging
|
||||
var w io.Writer
|
||||
w = os.Stdout
|
||||
|
||||
ctrl.SetLogger(zap.New(func(o *zap.Options) {
|
||||
o.Development = development
|
||||
o.DestWritter = w
|
||||
}))
|
||||
server := server.APIServer{}
|
||||
kubeClient, err := oam.InitKubeClient()
|
||||
if err != nil {
|
||||
ctrl.Log.Error(err, "failed to init an Kubernetes client")
|
||||
os.Exit(1)
|
||||
}
|
||||
errCh := make(chan error, 1)
|
||||
server.Launch(kubeClient, util.DefaultAPIServerPort, "", errCh)
|
||||
select {
|
||||
case err = <-errCh:
|
||||
ctrl.Log.Error(err, "failed to launch API server")
|
||||
}
|
||||
// handle signal: SIGTERM(15)
|
||||
sc := make(chan os.Signal, 1)
|
||||
signal.Notify(sc, syscall.SIGTERM)
|
||||
select {
|
||||
case <-sc:
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Minute)
|
||||
go server.Shutdown(ctx)
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,10 @@ import (
|
||||
// setup the gin http server handler
|
||||
|
||||
func setupRoute(kubeClient client.Client, staticPath string) http.Handler {
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
// if deploying static Dashboard, set the mode to `release`, or to `debug`
|
||||
if staticPath != "" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
// create the router
|
||||
router := gin.New()
|
||||
loggerConfig := gin.LoggerConfig{
|
||||
@@ -35,7 +36,10 @@ func setupRoute(kubeClient client.Client, staticPath string) http.Handler {
|
||||
)
|
||||
},
|
||||
}
|
||||
router.Use(static.Serve("/", static.LocalFile(staticPath, false)))
|
||||
|
||||
if staticPath != "" {
|
||||
router.Use(static.Serve("/", static.LocalFile(staticPath, false)))
|
||||
}
|
||||
|
||||
router.Use(gin.LoggerWithConfig(loggerConfig))
|
||||
router.Use(util.SetRequestID())
|
||||
@@ -69,7 +73,24 @@ func setupRoute(kubeClient client.Client, staticPath string) http.Handler {
|
||||
traitWorkload.POST("/", handler.AttachTrait)
|
||||
traitWorkload.DELETE("/:traitName", handler.DetachTrait)
|
||||
}
|
||||
|
||||
// component related operation
|
||||
components := apps.Group("/:appName/components")
|
||||
{
|
||||
components.GET("/:compName", handler.GetComponent)
|
||||
components.PUT("/:compName", handler.GetComponent)
|
||||
components.GET("/", handler.GetApp)
|
||||
components.DELETE("/:compName", handler.GetComponent)
|
||||
|
||||
traitWorkload := components.Group("/:compName/" + util.TraitDefinitionPath)
|
||||
{
|
||||
traitWorkload.POST("/", handler.AttachTrait)
|
||||
traitWorkload.DELETE("/:traitName", handler.DetachTrait)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// workload related api
|
||||
workload := api.Group(util.WorkloadDefinitionPath)
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
|
||||
var DefaultDashboardPort = ":38081"
|
||||
|
||||
const DefaultAPIServerPort = ":8081"
|
||||
|
||||
func AssembleResponse(c *gin.Context, data interface{}, err error) {
|
||||
var code = http.StatusOK
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user