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:
barnettZQG
2021-10-31 18:23:55 +08:00
committed by GitHub
parent 6666c3a2bb
commit 9519d2443a
20 changed files with 276 additions and 66 deletions

View File

@@ -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
View 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"]

View File

@@ -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:

View File

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

View File

@@ -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"`

View File

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

View File

@@ -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"`

View File

@@ -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, "/", "-")
}

View File

@@ -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 {

View File

@@ -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())
})

View File

@@ -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,

View File

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

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

View File

@@ -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)

View File

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

View File

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

View File

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

View File

@@ -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,

View File

@@ -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"`

View File

@@ -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())