mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
Feat: add alias field and fix some code bug (#2589)
* Feat: add alias field and fix some code bug * Fix: fix alias check rule bug * Fix: fix addon e2e test bug Co-authored-by: barnettZQG <yiyun.pro>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
ARG BASE_IMAGE="alpine:latest"
|
||||
# Build the manager binary
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.16-alpine as builder
|
||||
|
||||
@@ -24,15 +25,10 @@ RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
|
||||
go build -a -ldflags "-s -w -X github.com/oam-dev/kubevela/version.VelaVersion=${VERSION:-undefined} -X github.com/oam-dev/kubevela/version.GitRevision=${GITVERSION:-undefined}" \
|
||||
-o manager-${TARGETARCH} main.go
|
||||
|
||||
RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
|
||||
go build -a -ldflags "-s -w -X github.com/oam-dev/kubevela/version.VelaVersion=${VERSION:-undefined} -X github.com/oam-dev/kubevela/version.GitRevision=${GITVERSION:-undefined}" \
|
||||
-o apiserver-${TARGETARCH} cmd/apiserver/main.go
|
||||
|
||||
# Use alpine as base image due to the discussion in issue #1448
|
||||
# You can replace distroless as minimal base image to package the manager binary
|
||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||
# Overwrite `BASE_IMAGE` by passing `--build-arg=BASE_IMAGE=gcr.io/distroless/static:nonroot`
|
||||
ARG BASE_IMAGE
|
||||
FROM ${BASE_IMAGE:-alpine:latest}
|
||||
# This is required by daemon connnecting with cri
|
||||
RUN apk add --no-cache ca-certificates bash
|
||||
@@ -41,7 +37,6 @@ WORKDIR /
|
||||
|
||||
ARG TARGETARCH
|
||||
COPY --from=builder /workspace/manager-${TARGETARCH} /usr/local/bin/manager
|
||||
COPY --from=builder /workspace/apiserver-${TARGETARCH} /usr/local/bin/apiserver
|
||||
|
||||
COPY entrypoint.sh /usr/local/bin/
|
||||
|
||||
|
||||
48
Dockerfile.apiserver
Normal file
48
Dockerfile.apiserver
Normal file
@@ -0,0 +1,48 @@
|
||||
ARG BASE_IMAGE="alpine:latest"
|
||||
# Build the manager binary
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.16-alpine as builder
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-https://goproxy.cn}
|
||||
WORKDIR /workspace
|
||||
# Copy the Go Modules manifests
|
||||
COPY go.mod go.mod
|
||||
COPY go.sum go.sum
|
||||
# cache deps before building and copying source so that we don't need to re-download as much
|
||||
# and so that source changes don't invalidate our downloaded layer
|
||||
RUN go mod download
|
||||
|
||||
# Copy the go source
|
||||
COPY cmd/core/main.go main.go
|
||||
COPY cmd/apiserver/main.go cmd/apiserver/main.go
|
||||
COPY apis/ apis/
|
||||
COPY pkg/ pkg/
|
||||
COPY version/ version/
|
||||
|
||||
# Build
|
||||
ARG TARGETARCH
|
||||
ARG VERSION
|
||||
ARG GITVERSION
|
||||
|
||||
RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
|
||||
go build -a -ldflags "-s -w -X github.com/oam-dev/kubevela/version.VelaVersion=${VERSION:-undefined} -X github.com/oam-dev/kubevela/version.GitRevision=${GITVERSION:-undefined}" \
|
||||
-o apiserver-${TARGETARCH} cmd/apiserver/main.go
|
||||
|
||||
# Use alpine as base image due to the discussion in issue #1448
|
||||
# You can replace distroless as minimal base image to package the manager binary
|
||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||
# Overwrite `BASE_IMAGE` by passing `--build-arg=BASE_IMAGE=gcr.io/distroless/static:nonroot`
|
||||
|
||||
FROM ${BASE_IMAGE:-alpine:latest}
|
||||
# This is required by daemon connnecting with cri
|
||||
RUN apk add --no-cache ca-certificates bash
|
||||
|
||||
WORKDIR /
|
||||
|
||||
ARG TARGETARCH
|
||||
COPY --from=builder /workspace/apiserver-${TARGETARCH} /usr/local/bin/apiserver
|
||||
|
||||
COPY entrypoint.sh /usr/local/bin/
|
||||
|
||||
ENTRYPOINT ["entrypoint.sh"]
|
||||
|
||||
CMD ["apiserver"]
|
||||
7
Makefile
7
Makefile
@@ -38,6 +38,7 @@ endif
|
||||
# Image URL to use all building/pushing image targets
|
||||
VELA_CORE_IMAGE ?= vela-core:latest
|
||||
VELA_CORE_TEST_IMAGE ?= vela-core-test:$(GIT_COMMIT)
|
||||
VELA_APISERVER_IMAGE ?= apiserver:latest
|
||||
VELA_RUNTIME_ROLLOUT_IMAGE ?= vela-runtime-rollout:latest
|
||||
VELA_RUNTIME_ROLLOUT_TEST_IMAGE ?= vela-runtime-rollout-test:$(GIT_COMMIT)
|
||||
RUNTIME_CLUSTER_CONFIG ?= /tmp/worker.kubeconfig
|
||||
@@ -133,8 +134,12 @@ check-diff: reviewable
|
||||
@$(OK) branch is clean
|
||||
|
||||
# Build the docker image
|
||||
docker-build:
|
||||
docker-build: docker-build-core docker-build-apiserver
|
||||
@$(OK)
|
||||
docker-build-core:
|
||||
docker build --build-arg=VERSION=$(VELA_VERSION) --build-arg=GITVERSION=$(GIT_COMMIT) -t $(VELA_CORE_IMAGE) .
|
||||
docker-build-apiserver:
|
||||
docker build --build-arg=VERSION=$(VELA_VERSION) --build-arg=GITVERSION=$(GIT_COMMIT) -t $(VELA_APISERVER_IMAGE) -f Dockerfile.apiserver .
|
||||
|
||||
# Build the runtime docker image
|
||||
docker-build-runtime-rollout:
|
||||
|
||||
@@ -30,6 +30,7 @@ func init() {
|
||||
type Application struct {
|
||||
Model
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Namespace string `json:"namespace"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
@@ -82,6 +83,7 @@ type ApplicationComponent struct {
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Creator string `json:"creator"`
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Type string `json:"type"`
|
||||
|
||||
// ExternalRevision specified the component revisionName
|
||||
|
||||
@@ -22,10 +22,11 @@ func init() {
|
||||
|
||||
// ProviderInfo describes the information from provider API
|
||||
type ProviderInfo struct {
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Zone string `json:"zone"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
Provider string `json:"provider"`
|
||||
ClusterName string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Zone string `json:"zone"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -39,6 +40,7 @@ const (
|
||||
type Cluster struct {
|
||||
Model
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
|
||||
@@ -32,6 +32,7 @@ func init() {
|
||||
type Workflow struct {
|
||||
Model
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
Enable bool `json:"enable"`
|
||||
// Workflow used by the default
|
||||
|
||||
@@ -50,21 +50,23 @@ type EmptyResponse struct{}
|
||||
|
||||
// CreateAddonRegistryRequest defines the format for addon registry create request
|
||||
type CreateAddonRegistryRequest struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
|
||||
Git *model.GitAddonSource `json:"git,omitempty"`
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Git *model.GitAddonSource `json:"git,omitempty"`
|
||||
}
|
||||
|
||||
// AddonRegistryMeta defines the format for a single addon registry
|
||||
type AddonRegistryMeta struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Git *model.GitAddonSource `json:"git,omitempty"`
|
||||
}
|
||||
|
||||
Git *model.GitAddonSource `json:"git,omitempty"`
|
||||
// ListAddonRegistryResponse list addon registry
|
||||
type ListAddonRegistryResponse struct {
|
||||
Registrys []*AddonRegistryMeta `json:"registrys"`
|
||||
}
|
||||
|
||||
// EnableAddonRequest defines the format for enable addon request
|
||||
type EnableAddonRequest struct {
|
||||
|
||||
// Args is the key-value environment variables, e.g. AK/SK credentials.
|
||||
Args map[string]string `json:"args,omitempty"`
|
||||
}
|
||||
@@ -76,15 +78,11 @@ type ListAddonResponse struct {
|
||||
|
||||
// AddonMeta defines the format for a single addon
|
||||
type AddonMeta struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
Version string `json:"version"`
|
||||
|
||||
Description string `json:"description"`
|
||||
|
||||
Icon string `json:"icon"`
|
||||
|
||||
Tags []string `json:"tags"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// DetailAddonResponse defines the format for showing the addon details
|
||||
@@ -120,6 +118,7 @@ type AccessKeyRequest struct {
|
||||
// CreateClusterRequest request parameters to create a cluster
|
||||
type CreateClusterRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Icon string `json:"icon"`
|
||||
KubeConfig string `json:"kubeConfig,omitempty" validate:"required_without=KubeConfigSecret"`
|
||||
@@ -134,6 +133,7 @@ type ConnectCloudClusterRequest struct {
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
ClusterID string `json:"clusterID"`
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Icon string `json:"icon"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
@@ -174,6 +174,7 @@ type ListCloudClusterResponse struct {
|
||||
// ClusterBase cluster base model
|
||||
type ClusterBase struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
@@ -186,14 +187,35 @@ type ClusterBase struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// ListApplicatioOptions list application query options
|
||||
type ListApplicatioOptions struct {
|
||||
Namespace string `json:"namespace"`
|
||||
Cluster string `json:"cluster"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
// ListApplicationResponse list applications by query params
|
||||
type ListApplicationResponse struct {
|
||||
Applications []*ApplicationBase `json:"applications"`
|
||||
}
|
||||
|
||||
// EnvBindList env bind list
|
||||
type EnvBindList []*EnvBind
|
||||
|
||||
// ContainCluster contain cluster name
|
||||
func (e EnvBindList) ContainCluster(name string) bool {
|
||||
for _, eb := range e {
|
||||
if eb.ClusterSelector != nil && eb.ClusterSelector.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ApplicationBase application base model
|
||||
type ApplicationBase struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Namespace string `json:"namespace"`
|
||||
Description string `json:"description"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
@@ -201,7 +223,7 @@ type ApplicationBase struct {
|
||||
Icon string `json:"icon"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Status string `json:"status"`
|
||||
EnvBind []*EnvBind `json:"envBind,omitempty"`
|
||||
EnvBind EnvBindList `json:"envBind,omitempty"`
|
||||
GatewayRuleList []GatewayRule `json:"gatewayRule"`
|
||||
}
|
||||
|
||||
@@ -227,6 +249,7 @@ type GatewayRule struct {
|
||||
// CreateApplicationRequest create application request body
|
||||
type CreateApplicationRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Namespace string `json:"namespace" validate:"checkname"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
@@ -276,6 +299,7 @@ type ApplicationResourceInfo struct {
|
||||
// ComponentBase component base model
|
||||
type ComponentBase struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
ComponentType string `json:"componentType"`
|
||||
@@ -296,6 +320,7 @@ type ComponentListResponse struct {
|
||||
// CreateComponentRequest create component request model
|
||||
type CreateComponentRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
@@ -431,6 +456,7 @@ type PolicyDefinition struct {
|
||||
type CreateWorkflowRequest struct {
|
||||
AppName string `json:"appName" validate:"checkname"`
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Description string `json:"description"`
|
||||
Steps []WorkflowStep `json:"steps,omitempty"`
|
||||
Enable bool `json:"enable"`
|
||||
@@ -439,6 +465,7 @@ type CreateWorkflowRequest struct {
|
||||
|
||||
// UpdateWorkflowRequest update or create application workflow
|
||||
type UpdateWorkflowRequest struct {
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Description string `json:"description"`
|
||||
Steps []WorkflowStep `json:"steps,omitempty"`
|
||||
Enable bool `json:"enable"`
|
||||
@@ -471,6 +498,7 @@ type ListWorkflowResponse struct {
|
||||
// WorkflowBase workflow base model
|
||||
type WorkflowBase struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
Enable bool `json:"enable"`
|
||||
Default bool `json:"default"`
|
||||
|
||||
@@ -5,27 +5,28 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"golang.org/x/oauth2"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/google/go-github/v32/github"
|
||||
"golang.org/x/oauth2"
|
||||
errors2 "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/google/go-github/v32/github"
|
||||
common2 "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
restutils "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
@@ -46,7 +47,8 @@ const (
|
||||
type AddonUsecase interface {
|
||||
GetAddonRegistryModel(ctx context.Context, name string) (*model.AddonRegistry, error)
|
||||
CreateAddonRegistry(ctx context.Context, req apis.CreateAddonRegistryRequest) (*apis.AddonRegistryMeta, error)
|
||||
ListAddons(ctx context.Context, detailed bool) ([]*apis.DetailAddonResponse, error)
|
||||
ListAddonRegistries(ctx context.Context) ([]*apis.AddonRegistryMeta, error)
|
||||
ListAddons(ctx context.Context, detailed bool, query string) ([]*apis.DetailAddonResponse, error)
|
||||
StatusAddon(name string) (*apis.AddonStatusResponse, error)
|
||||
GetAddon(ctx context.Context, name string) (*apis.DetailAddonResponse, error)
|
||||
EnableAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error
|
||||
@@ -73,7 +75,7 @@ type addonUsecaseImpl struct {
|
||||
}
|
||||
|
||||
func (u *addonUsecaseImpl) GetAddon(ctx context.Context, name string) (*apis.DetailAddonResponse, error) {
|
||||
addons, err := u.ListAddons(ctx, true)
|
||||
addons, err := u.ListAddons(ctx, true, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -121,7 +123,7 @@ func (u *addonUsecaseImpl) StatusAddon(name string) (*apis.AddonStatusResponse,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *addonUsecaseImpl) ListAddons(ctx context.Context, detailed bool) ([]*apis.DetailAddonResponse, error) {
|
||||
func (u *addonUsecaseImpl) ListAddons(ctx context.Context, detailed bool, query string) ([]*apis.DetailAddonResponse, error) {
|
||||
// Backward compatibility with ConfigMap addons.
|
||||
// We will deprecate ConfigMap and use Git based registry.
|
||||
addons, err := getAddonsFromConfigMap(detailed)
|
||||
@@ -129,17 +131,27 @@ func (u *addonUsecaseImpl) ListAddons(ctx context.Context, detailed bool) ([]*ap
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rs, err := u.listAddonRegistries(ctx)
|
||||
rs, err := u.ListAddonRegistries(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range rs {
|
||||
gitAddons, err := getAddonsFromGit(r.Git.URL, r.Git.Path, r.Git.Token, detailed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Logger.Errorf("list addons from registry %s failure %s", r.Name, err.Error())
|
||||
continue
|
||||
}
|
||||
addons = mergeAddons(addons, gitAddons)
|
||||
}
|
||||
if query != "" {
|
||||
var new []*apis.DetailAddonResponse
|
||||
for i, addon := range addons {
|
||||
if strings.Contains(addon.Name, query) || strings.Contains(addon.Description, query) {
|
||||
new = append(new, addons[i])
|
||||
}
|
||||
}
|
||||
addons = new
|
||||
}
|
||||
sort.Slice(addons, func(i, j int) bool {
|
||||
return addons[i].Name < addons[j].Name
|
||||
})
|
||||
@@ -175,7 +187,7 @@ func (u *addonUsecaseImpl) GetAddonRegistryModel(ctx context.Context, name strin
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func (u *addonUsecaseImpl) listAddonRegistries(ctx context.Context) ([]*apis.AddonRegistryMeta, error) {
|
||||
func (u *addonUsecaseImpl) ListAddonRegistries(ctx context.Context) ([]*apis.AddonRegistryMeta, error) {
|
||||
var r = model.AddonRegistry{}
|
||||
entities, err := u.ds.List(ctx, &r, &datastore.ListOptions{})
|
||||
if err != nil {
|
||||
@@ -307,6 +319,7 @@ func getAddonsFromGit(baseURL, dir, token string, detailed bool) ([]*apis.Detail
|
||||
)
|
||||
tc = oauth2.NewClient(context.Background(), ts)
|
||||
}
|
||||
tc.Timeout = time.Second * 10
|
||||
clt := github.NewClient(tc)
|
||||
// TODO add error handling
|
||||
baseURL = strings.TrimSuffix(baseURL, ".git")
|
||||
@@ -329,7 +342,7 @@ func getAddonsFromGit(baseURL, dir, token string, detailed bool) ([]*apis.Detail
|
||||
}
|
||||
addonRes := apis.DetailAddonResponse{
|
||||
AddonMeta: apis.AddonMeta{
|
||||
Name: *subItems.Name,
|
||||
Name: converAddonName(*subItems.Name),
|
||||
},
|
||||
}
|
||||
var err error
|
||||
@@ -384,7 +397,7 @@ func getAddonsFromConfigMap(detailed bool) ([]*apis.DetailAddonResponse, error)
|
||||
for _, addon := range cliAddons {
|
||||
d := &apis.DetailAddonResponse{
|
||||
AddonMeta: apis.AddonMeta{
|
||||
Name: addon.Name,
|
||||
Name: converAddonName(addon.Name),
|
||||
// TODO add actual Version, Icon, tags
|
||||
Version: "v1alpha1",
|
||||
Description: addon.Description,
|
||||
@@ -399,5 +412,8 @@ func getAddonsFromConfigMap(detailed bool) ([]*apis.DetailAddonResponse, error)
|
||||
addons = append(addons, d)
|
||||
}
|
||||
return addons, nil
|
||||
|
||||
}
|
||||
|
||||
func converAddonName(name string) string {
|
||||
return strings.ReplaceAll(name, "/", "-")
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -52,7 +53,7 @@ const (
|
||||
|
||||
// ApplicationUsecase application usecase
|
||||
type ApplicationUsecase interface {
|
||||
ListApplications(ctx context.Context) ([]*apisv1.ApplicationBase, error)
|
||||
ListApplications(ctx context.Context, listOptions apisv1.ListApplicatioOptions) ([]*apisv1.ApplicationBase, error)
|
||||
GetApplication(ctx context.Context, appName string) (*model.Application, error)
|
||||
DetailApplication(ctx context.Context, app *model.Application) (*apisv1.DetailApplicationResponse, error)
|
||||
PublishApplicationTemplate(ctx context.Context, app *model.Application) (*apisv1.ApplicationTemplateBase, error)
|
||||
@@ -92,15 +93,28 @@ func NewApplicationUsecase(ds datastore.DataStore, workflowUsecase WorkflowUseca
|
||||
}
|
||||
|
||||
// ListApplications list applications
|
||||
func (c *applicationUsecaseImpl) ListApplications(ctx context.Context) ([]*apisv1.ApplicationBase, error) {
|
||||
func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptions apisv1.ListApplicatioOptions) ([]*apisv1.ApplicationBase, error) {
|
||||
var app = model.Application{}
|
||||
if listOptions.Namespace != "" {
|
||||
app.Namespace = listOptions.Namespace
|
||||
}
|
||||
entitys, err := c.ds.List(ctx, &app, &datastore.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []*apisv1.ApplicationBase
|
||||
for _, entity := range entitys {
|
||||
list = append(list, c.converAppModelToBase(ctx, entity.(*model.Application)))
|
||||
appBase := c.converAppModelToBase(ctx, entity.(*model.Application))
|
||||
if listOptions.Query != "" &&
|
||||
!(strings.Contains(appBase.Alias, listOptions.Query) ||
|
||||
strings.Contains(appBase.Name, listOptions.Query) ||
|
||||
strings.Contains(appBase.Description, listOptions.Query)) {
|
||||
continue
|
||||
}
|
||||
if listOptions.Cluster != "" && !appBase.EnvBind.ContainCluster(listOptions.Cluster) {
|
||||
continue
|
||||
}
|
||||
list = append(list, appBase)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
@@ -152,6 +166,7 @@ func (c *applicationUsecaseImpl) PublishApplicationTemplate(ctx context.Context,
|
||||
func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apisv1.CreateApplicationRequest) (*apisv1.ApplicationBase, error) {
|
||||
application := model.Application{
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
Namespace: req.Namespace,
|
||||
Icon: req.Icon,
|
||||
@@ -357,6 +372,7 @@ func (c *applicationUsecaseImpl) DetailComponent(ctx context.Context, app *model
|
||||
func (c *applicationUsecaseImpl) converComponentModelToBase(m *model.ApplicationComponent) *apisv1.ComponentBase {
|
||||
return &apisv1.ComponentBase{
|
||||
Name: m.Name,
|
||||
Alias: m.Alias,
|
||||
Description: m.Description,
|
||||
Labels: m.Labels,
|
||||
ComponentType: m.Type,
|
||||
@@ -629,6 +645,7 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo
|
||||
func (c *applicationUsecaseImpl) converAppModelToBase(ctx context.Context, app *model.Application) *apisv1.ApplicationBase {
|
||||
appBeas := &apisv1.ApplicationBase{
|
||||
Name: app.Name,
|
||||
Alias: app.Alias,
|
||||
Namespace: app.Namespace,
|
||||
CreateTime: app.CreateTime,
|
||||
UpdateTime: app.UpdateTime,
|
||||
@@ -720,6 +737,7 @@ func (c *applicationUsecaseImpl) AddComponent(ctx context.Context, app *model.Ap
|
||||
Name: com.Name,
|
||||
Type: com.ComponentType,
|
||||
DependsOn: com.DependsOn,
|
||||
Alias: com.Alias,
|
||||
}
|
||||
properties, err := model.NewJSONStructByString(com.Properties)
|
||||
if err != nil {
|
||||
|
||||
@@ -140,7 +140,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test ListApplications function", func() {
|
||||
apps, err := appUsecase.ListApplications(context.TODO())
|
||||
apps, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicatioOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(apps), 3)).Should(BeEmpty())
|
||||
})
|
||||
|
||||
@@ -174,6 +174,7 @@ func createClusterModelFromRequest(req apis.CreateClusterRequest, oldCluster *mo
|
||||
newCluster = &model.Cluster{}
|
||||
}
|
||||
newCluster.Name = req.Name
|
||||
newCluster.Alias = req.Alias
|
||||
newCluster.Description = req.Description
|
||||
newCluster.Icon = req.Icon
|
||||
newCluster.Labels = req.Labels
|
||||
@@ -194,10 +195,11 @@ func (c *clusterUsecaseImpl) createKubeCluster(ctx context.Context, req apis.Cre
|
||||
cluster.SetUpdateTime(t)
|
||||
if providerCluster != nil {
|
||||
cluster.Provider = model.ProviderInfo{
|
||||
Name: providerCluster.Name,
|
||||
ID: providerCluster.ID,
|
||||
Zone: providerCluster.Zone,
|
||||
Labels: providerCluster.Labels,
|
||||
Provider: providerCluster.Provider,
|
||||
ClusterName: providerCluster.Name,
|
||||
ID: providerCluster.ID,
|
||||
Zone: providerCluster.Zone,
|
||||
Labels: providerCluster.Labels,
|
||||
}
|
||||
cluster.DashboardURL = providerCluster.DashBoardURL
|
||||
}
|
||||
@@ -431,6 +433,7 @@ func (c *clusterUsecaseImpl) ConnectCloudCluster(ctx context.Context, provider s
|
||||
}
|
||||
createReq := apis.CreateClusterRequest{
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
Icon: req.Icon,
|
||||
Labels: req.Labels,
|
||||
@@ -442,6 +445,7 @@ func (c *clusterUsecaseImpl) ConnectCloudCluster(ctx context.Context, provider s
|
||||
func newClusterBaseFromCluster(cluster *model.Cluster) *apis.ClusterBase {
|
||||
return &apis.ClusterBase{
|
||||
Name: cluster.Name,
|
||||
Alias: cluster.Alias,
|
||||
Description: cluster.Description,
|
||||
Icon: cluster.Icon,
|
||||
Labels: cluster.Labels,
|
||||
|
||||
@@ -19,9 +19,9 @@ package bcode
|
||||
var (
|
||||
|
||||
// ErrAddonNotExist addon not exist
|
||||
ErrAddonNotExist = NewBcode(400, 50001, "addon not exist")
|
||||
ErrAddonNotExist = NewBcode(404, 50001, "addon not exist")
|
||||
|
||||
// ErrAddonRegistryExist application is exist
|
||||
// ErrAddonRegistryExist addon is exist
|
||||
ErrAddonRegistryExist = NewBcode(400, 50002, "addon name already exists")
|
||||
|
||||
// ErrAddonRenderFail fail to render addon application
|
||||
|
||||
52
pkg/apiserver/rest/utils/uiswagger.go
Normal file
52
pkg/apiserver/rest/utils/uiswagger.go
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela 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 utils
|
||||
|
||||
// UIParameter Structured import table simple UI model
|
||||
type UIParameter struct {
|
||||
Label string `json:"label"`
|
||||
Description string `json:"description"`
|
||||
Validate *Validate `json:"validete,omitempty"`
|
||||
JSONKey string `json:"jsonKey"`
|
||||
UIType string `json:"uiType"`
|
||||
// means only can be read.
|
||||
Disable bool `json:"disable"`
|
||||
SubParameters []*UIParameter `json:"subParameters,omitempty"`
|
||||
}
|
||||
|
||||
// Validate parameter validate rule
|
||||
type Validate struct {
|
||||
Required bool `json:"required,omitempty"`
|
||||
Max int `json:"max,omitempty"`
|
||||
Min int `json:"min,omitempty"`
|
||||
Regular string `json:"regular,omitempty"`
|
||||
Options []*Options `json:"options,omitempty"`
|
||||
DefaultValue interface{} `json:"defaultValue,omitempty"`
|
||||
}
|
||||
|
||||
// Options select option
|
||||
type Options struct {
|
||||
Label string `json:"label"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// ParseUIParameterFromDefinition cue of parameter in Definitions was analyzed to obtain the form description model.
|
||||
func ParseUIParameterFromDefinition(definition []byte) ([]*UIParameter, error) {
|
||||
var params []*UIParameter
|
||||
|
||||
return params, nil
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package webservice
|
||||
import (
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
@@ -48,6 +49,7 @@ func (s *addonWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(s.listAddons).
|
||||
Doc("list all addons").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("query", "Fuzzy search based on name and description.").DataType("string")).
|
||||
Returns(200, "", apis.ListAddonResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.ListAddonResponse{}))
|
||||
@@ -59,41 +61,41 @@ func (s *addonWebService) GetWebService() *restful.WebService {
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.DetailAddonResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Param(ws.QueryParameter("name", "addon name to query detail").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("name", "addon name to query detail").DataType("string").Required(true)).
|
||||
Writes(apis.DetailAddonResponse{}))
|
||||
|
||||
// GET status
|
||||
ws.Route(ws.GET("/status").To(s.statusAddon).
|
||||
ws.Route(ws.GET("/{name}/status").To(s.statusAddon).
|
||||
Doc("show status of an addon").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.AddonStatusResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Param(ws.QueryParameter("name", "addon name to query status").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("name", "addon name to query status").DataType("string").Required(true)).
|
||||
Writes(apis.AddonStatusResponse{}))
|
||||
|
||||
// enable addon
|
||||
ws.Route(ws.POST("/enable").To(s.enableAddon).
|
||||
ws.Route(ws.POST("/{name}/enable").To(s.enableAddon).
|
||||
Doc("enable an addon").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.AddonStatusResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Param(ws.QueryParameter("name", "addon name to enable").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("name", "addon name to enable").DataType("string").Required(true)).
|
||||
Writes(apis.AddonStatusResponse{}))
|
||||
|
||||
// disable addon
|
||||
ws.Route(ws.POST("/disable").To(s.disableAddon).
|
||||
ws.Route(ws.POST("/{name}/disable").To(s.disableAddon).
|
||||
Doc("disable an addon").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.AddonStatusResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Param(ws.QueryParameter("name", "addon name to enable").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("name", "addon name to enable").DataType("string").Required(true)).
|
||||
Writes(apis.AddonStatusResponse{}))
|
||||
|
||||
return ws
|
||||
}
|
||||
|
||||
func (s *addonWebService) listAddons(req *restful.Request, res *restful.Response) {
|
||||
detailAddons, err := s.addonUsecase.ListAddons(req.Request.Context(), false)
|
||||
detailAddons, err := s.addonUsecase.ListAddons(req.Request.Context(), false, req.QueryParameter("query"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -113,7 +115,7 @@ func (s *addonWebService) listAddons(req *restful.Request, res *restful.Response
|
||||
}
|
||||
|
||||
func (s *addonWebService) detailAddon(req *restful.Request, res *restful.Response) {
|
||||
name := req.QueryParameter("name")
|
||||
name := req.PathParameter("name")
|
||||
addon, err := s.addonUsecase.GetAddon(req.Request.Context(), name)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
@@ -140,7 +142,7 @@ func (s *addonWebService) enableAddon(req *restful.Request, res *restful.Respons
|
||||
return
|
||||
}
|
||||
|
||||
name := req.QueryParameter("name")
|
||||
name := req.PathParameter("name")
|
||||
err = s.addonUsecase.EnableAddon(req.Request.Context(), name, createReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
@@ -151,7 +153,7 @@ func (s *addonWebService) enableAddon(req *restful.Request, res *restful.Respons
|
||||
}
|
||||
|
||||
func (s *addonWebService) disableAddon(req *restful.Request, res *restful.Response) {
|
||||
name := req.QueryParameter("name")
|
||||
name := req.PathParameter("name")
|
||||
err := s.addonUsecase.DisableAddon(req.Request.Context(), name)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
@@ -161,7 +163,7 @@ func (s *addonWebService) disableAddon(req *restful.Request, res *restful.Respon
|
||||
}
|
||||
|
||||
func (s *addonWebService) statusAddon(req *restful.Request, res *restful.Response) {
|
||||
name := req.QueryParameter("name")
|
||||
name := req.PathParameter("name")
|
||||
status, err := s.addonUsecase.StatusAddon(name)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
|
||||
@@ -56,6 +56,13 @@ func (s *addonRegistryWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.AddonRegistryMeta{}))
|
||||
|
||||
ws.Route(ws.GET("/").To(s.listAddonRegistry).
|
||||
Doc("list all addon registry").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.ListAddonRegistryResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.ListAddonRegistryResponse{}))
|
||||
|
||||
// Delete
|
||||
ws.Route(ws.DELETE("/{name}").To(s.deleteAddonRegistry).
|
||||
Doc("delete an addon registry").
|
||||
@@ -107,3 +114,15 @@ func (s *addonRegistryWebService) deleteAddonRegistry(req *restful.Request, res
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *addonRegistryWebService) listAddonRegistry(req *restful.Request, res *restful.Response) {
|
||||
registrys, err := s.addonUsecase.ListAddonRegistries(req.Request.Context())
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(apis.ListAddonRegistryResponse{Registrys: registrys}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +222,11 @@ func (c *applicationWebService) createApplication(req *restful.Request, res *res
|
||||
}
|
||||
|
||||
func (c *applicationWebService) listApplications(req *restful.Request, res *restful.Response) {
|
||||
apps, err := c.applicationUsecase.ListApplications(req.Request.Context())
|
||||
apps, err := c.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicatioOptions{
|
||||
Namespace: req.QueryParameter("namespace"),
|
||||
Cluster: req.QueryParameter("cluster"),
|
||||
Query: req.QueryParameter("query"),
|
||||
})
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
|
||||
@@ -35,6 +35,9 @@ func init() {
|
||||
if err := validate.RegisterValidation("checkname", ValidateName); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := validate.RegisterValidation("checkalias", ValidateAlias); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateName custom check name field
|
||||
@@ -45,3 +48,12 @@ func ValidateName(fl validator.FieldLevel) bool {
|
||||
}
|
||||
return nameRegexp.MatchString(value)
|
||||
}
|
||||
|
||||
// ValidateAlias custom check alias field
|
||||
func ValidateAlias(fl validator.FieldLevel) bool {
|
||||
value := fl.Field().String()
|
||||
if value != "" && (len(value) > 64 || len(value) < 2) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -116,6 +116,7 @@ func (provider *AliyunCloudProvider) GetClusterInfo(clusterID string) (*CloudClu
|
||||
labels := provider.decodeClusterLabels(cluster.Tags)
|
||||
url := provider.decodeClusterURL(*cluster.MasterUrl)
|
||||
return &CloudCluster{
|
||||
Provider: ProviderAliyun,
|
||||
ID: *cluster.ClusterId,
|
||||
Name: *cluster.Name,
|
||||
Type: *cluster.ClusterType,
|
||||
|
||||
@@ -23,6 +23,7 @@ const (
|
||||
|
||||
// CloudCluster describes the interface that cloud provider should return
|
||||
type CloudCluster struct {
|
||||
Provider string `json:"provider"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
|
||||
@@ -85,7 +85,7 @@ var _ = Describe("Test addon rest api", func() {
|
||||
Args: map[string]string{},
|
||||
}
|
||||
testAddon := "fluxcd"
|
||||
res := post("/api/v1/addons/enable?name="+testAddon, req)
|
||||
res := post("/api/v1/addons/"+testAddon+"/enable", req)
|
||||
Expect(res).ShouldNot(BeNil())
|
||||
Expect(res.StatusCode).Should(Equal(200))
|
||||
Expect(res.Body).ShouldNot(BeNil())
|
||||
@@ -103,7 +103,7 @@ var _ = Describe("Test addon rest api", func() {
|
||||
period := 20 * time.Second
|
||||
timeout := 5 * time.Minute
|
||||
err = wait.PollImmediate(period, timeout, func() (done bool, err error) {
|
||||
res = get("/api/v1/addons/status?name=" + testAddon)
|
||||
res = get("/api/v1/addons/" + testAddon + "/status")
|
||||
err = json.NewDecoder(res.Body).Decode(&statusRes)
|
||||
Expect(err).Should(BeNil())
|
||||
if statusRes.Phase == apis.AddonPhaseEnabled {
|
||||
@@ -113,7 +113,7 @@ var _ = Describe("Test addon rest api", func() {
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
res = post("/api/v1/addons/disable?name="+testAddon, req)
|
||||
res = post("/api/v1/addons/"+testAddon+"/disable", req)
|
||||
Expect(res).ShouldNot(BeNil())
|
||||
Expect(res.StatusCode).Should(Equal(200))
|
||||
Expect(res.Body).ShouldNot(BeNil())
|
||||
|
||||
Reference in New Issue
Block a user