mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-19 04:26:39 +00:00
Feat: list enabled addon from cluster interface and update addon (#2832)
make reviewable lint Feat: interface for update addon nit import fix comments fix status args return Signed-off-by: wangyike <wangyike_wyk@163.com>
This commit is contained in:
+13
-15
@@ -50,6 +50,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
@@ -746,24 +747,26 @@ func Convert2SecName(name string) string {
|
||||
type Handler struct {
|
||||
ctx context.Context
|
||||
addon *types.Addon
|
||||
clt client.Client
|
||||
cli client.Client
|
||||
apply apply.Applicator
|
||||
source *GitAddonSource
|
||||
args map[string]interface{}
|
||||
}
|
||||
|
||||
func newAddonHandler(ctx context.Context, addon *types.Addon, clt client.Client, source *GitAddonSource, args map[string]interface{}) Handler {
|
||||
func newAddonHandler(ctx context.Context, addon *types.Addon, cli client.Client, apply apply.Applicator, source *GitAddonSource, args map[string]interface{}) Handler {
|
||||
return Handler{
|
||||
ctx: ctx,
|
||||
addon: addon,
|
||||
clt: clt,
|
||||
cli: cli,
|
||||
apply: apply,
|
||||
source: source,
|
||||
args: args,
|
||||
}
|
||||
}
|
||||
|
||||
// EnableAddon will enable addon with dependency check, source is where addon from.
|
||||
func EnableAddon(ctx context.Context, addon *types.Addon, clt client.Client, source *GitAddonSource, args map[string]interface{}) error {
|
||||
h := newAddonHandler(ctx, addon, clt, source, args)
|
||||
func EnableAddon(ctx context.Context, addon *types.Addon, cli client.Client, apply apply.Applicator, source *GitAddonSource, args map[string]interface{}) error {
|
||||
h := newAddonHandler(ctx, addon, cli, apply, source, args)
|
||||
err := h.enableAddon()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -786,7 +789,7 @@ func (h *Handler) enableAddon() error {
|
||||
func (h *Handler) checkDependencies() error {
|
||||
var app v1beta1.Application
|
||||
for _, dep := range h.addon.Dependencies {
|
||||
err := h.clt.Get(h.ctx, client.ObjectKey{
|
||||
err := h.cli.Get(h.ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: Convert2AppName(dep.Name),
|
||||
}, &app)
|
||||
@@ -821,19 +824,14 @@ func (h *Handler) dispatchAddonResource() error {
|
||||
return errors.Wrap(err, "render addon application fail")
|
||||
}
|
||||
|
||||
err = h.clt.Get(h.ctx, client.ObjectKeyFromObject(app), app)
|
||||
if err == nil {
|
||||
return errors.New("addon is already enabled")
|
||||
}
|
||||
|
||||
err = h.clt.Create(h.ctx, app)
|
||||
err = h.apply.Apply(h.ctx, app)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fail to create application")
|
||||
}
|
||||
|
||||
for _, def := range defs {
|
||||
addOwner(def, app)
|
||||
err = h.clt.Create(h.ctx, def)
|
||||
err = h.apply.Apply(h.ctx, def)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -841,7 +839,7 @@ func (h *Handler) dispatchAddonResource() error {
|
||||
|
||||
for _, schema := range schemas {
|
||||
addOwner(schema, app)
|
||||
err = h.clt.Create(h.ctx, schema)
|
||||
err = h.apply.Apply(h.ctx, schema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -850,7 +848,7 @@ func (h *Handler) dispatchAddonResource() error {
|
||||
if h.args != nil && len(h.args) > 0 {
|
||||
sec := RenderArgsSecret(h.addon, h.args)
|
||||
addOwner(sec, app)
|
||||
err = h.clt.Create(h.ctx, sec)
|
||||
err = h.apply.Apply(h.ctx, sec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -91,6 +91,11 @@ type ListAddonResponse struct {
|
||||
Addons []*types.AddonMeta `json:"addons"`
|
||||
}
|
||||
|
||||
// ListEnabledAddonResponse defines the format for enabled addon list response
|
||||
type ListEnabledAddonResponse struct {
|
||||
EnabledAddons []*AddonStatusResponse
|
||||
}
|
||||
|
||||
// DetailAddonResponse defines the format for showing the addon details
|
||||
type DetailAddonResponse struct {
|
||||
types.AddonMeta
|
||||
@@ -113,6 +118,7 @@ type AddonDefinition struct {
|
||||
|
||||
// AddonStatusResponse defines the format of addon status response
|
||||
type AddonStatusResponse struct {
|
||||
Name string `json:"name"`
|
||||
Phase AddonPhase `json:"phase"`
|
||||
Args map[string]string `json:"args"`
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import (
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
restutils "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
@@ -57,6 +58,8 @@ type AddonUsecase interface {
|
||||
GetAddon(ctx context.Context, name string, registry string) (*apis.DetailAddonResponse, error)
|
||||
EnableAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error
|
||||
DisableAddon(ctx context.Context, name string) error
|
||||
ListEnabledAddon(ctx context.Context) ([]*apis.AddonStatusResponse, error)
|
||||
UpdateAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error
|
||||
}
|
||||
|
||||
// AddonImpl2AddonRes convert types.Addon to the type apiserver need
|
||||
@@ -165,31 +168,25 @@ func (u *addonUsecaseImpl) StatusAddon(ctx context.Context, name string) (*apis.
|
||||
return nil, bcode.ErrGetAddonApplication
|
||||
}
|
||||
|
||||
switch app.Status.Phase {
|
||||
case common2.ApplicationRunning:
|
||||
res := apis.AddonStatusResponse{
|
||||
Phase: apis.AddonPhaseEnabled,
|
||||
EnablingProgress: nil,
|
||||
}
|
||||
var sec v1.Secret
|
||||
err := u.kubeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: pkgaddon.Convert2SecName(name),
|
||||
}, &sec)
|
||||
if err != nil {
|
||||
return nil, bcode.ErrAddonSecretGet
|
||||
}
|
||||
res.Args = make(map[string]string, len(sec.Data))
|
||||
for k, v := range sec.Data {
|
||||
res.Args[k] = string(v)
|
||||
}
|
||||
return &res, nil
|
||||
default:
|
||||
return &apis.AddonStatusResponse{
|
||||
Phase: apis.AddonPhaseEnabling,
|
||||
EnablingProgress: nil,
|
||||
}, nil
|
||||
res := apis.AddonStatusResponse{
|
||||
Phase: convertAppStateToAddonPhase(app.Status.Phase),
|
||||
EnablingProgress: nil,
|
||||
}
|
||||
var sec v1.Secret
|
||||
err = u.kubeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: pkgaddon.Convert2SecName(name),
|
||||
}, &sec)
|
||||
if err != nil {
|
||||
return nil, bcode.ErrAddonSecretGet
|
||||
}
|
||||
|
||||
res.Args = make(map[string]string, len(sec.Data))
|
||||
for k, v := range sec.Data {
|
||||
res.Args[k] = string(v)
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (u *addonUsecaseImpl) ListAddons(ctx context.Context, registry, query string) ([]*apis.DetailAddonResponse, error) {
|
||||
@@ -360,7 +357,7 @@ func (u *addonUsecaseImpl) EnableAddon(ctx context.Context, name string, args ap
|
||||
continue
|
||||
}
|
||||
|
||||
err = pkgaddon.EnableAddon(ctx, addon, u.kubeClient, r.Git, args.Args)
|
||||
err = pkgaddon.EnableAddon(ctx, addon, u.kubeClient, u.apply, r.Git, args.Args)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("err when enable addon: %v", err)
|
||||
return bcode.ErrAddonApply
|
||||
@@ -402,6 +399,66 @@ func (u *addonUsecaseImpl) DisableAddon(ctx context.Context, name string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *addonUsecaseImpl) ListEnabledAddon(ctx context.Context) ([]*apis.AddonStatusResponse, error) {
|
||||
apps := &v1beta1.ApplicationList{}
|
||||
if err := u.kubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), client.HasLabels{oam.LabelAddonName}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []*apis.AddonStatusResponse
|
||||
for _, application := range apps.Items {
|
||||
if addonName := application.Labels[oam.LabelAddonName]; addonName != "" {
|
||||
if application.Status.Phase != common2.ApplicationRunning {
|
||||
continue
|
||||
}
|
||||
response = append(response, &apis.AddonStatusResponse{
|
||||
Name: addonName,
|
||||
Phase: convertAppStateToAddonPhase(application.Status.Phase),
|
||||
})
|
||||
}
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (u *addonUsecaseImpl) UpdateAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error {
|
||||
|
||||
var app v1beta1.Application
|
||||
// check addon application whether exist
|
||||
err := u.kubeClient.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: pkgaddon.Convert2AppName(name),
|
||||
}, &app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var addon *types.Addon
|
||||
registries, err := u.ListAddonRegistries(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, r := range registries {
|
||||
var exist bool
|
||||
if addon, exist = u.tryGetAddonFromCache(r.Name, name); !exist {
|
||||
addon, err = pkgaddon.GetAddon(name, r.Git, pkgaddon.EnableLevelOptions)
|
||||
}
|
||||
if err != nil && !errors.Is(err, pkgaddon.ErrNotExist) {
|
||||
return bcode.WrapGithubRateLimitErr(err)
|
||||
}
|
||||
if addon == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err = pkgaddon.EnableAddon(ctx, addon, u.kubeClient, u.apply, r.Git, args.Args)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("err when enable addon: %v", err)
|
||||
return bcode.ErrAddonApply
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return bcode.ErrAddonNotExist
|
||||
}
|
||||
|
||||
func addonRegistryModelFromCreateAddonRegistryRequest(req apis.CreateAddonRegistryRequest) *model.AddonRegistry {
|
||||
return &model.AddonRegistry{
|
||||
Name: req.Name,
|
||||
@@ -435,3 +492,12 @@ func ConvertAddonRegistryModel2AddonRegistryMeta(r *model.AddonRegistry) *apis.A
|
||||
Git: r.Git,
|
||||
}
|
||||
}
|
||||
|
||||
func convertAppStateToAddonPhase(state common2.ApplicationPhase) apis.AddonPhase {
|
||||
switch state {
|
||||
case common2.ApplicationRunning:
|
||||
return apis.AddonPhaseEnabled
|
||||
default:
|
||||
return apis.AddonPhaseEnabling
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,13 @@ func NewAddonWebService(u usecase.AddonUsecase) WebService {
|
||||
}
|
||||
}
|
||||
|
||||
// NewEnabledAddonWebService returns enabled addon web service
|
||||
func NewEnabledAddonWebService(u usecase.AddonUsecase) WebService {
|
||||
return &enabledAddonWebService{
|
||||
addonUsecase: u,
|
||||
}
|
||||
}
|
||||
|
||||
type addonWebService struct {
|
||||
addonUsecase usecase.AddonUsecase
|
||||
}
|
||||
@@ -96,6 +103,16 @@ func (s *addonWebService) GetWebService() *restful.WebService {
|
||||
Param(ws.PathParameter("name", "addon name to enable").DataType("string").Required(true)).
|
||||
Writes(apis.AddonStatusResponse{}))
|
||||
|
||||
// update addon
|
||||
ws.Route(ws.PUT("/{name}/update").To(s.updateAddon).
|
||||
Doc("update an addon").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.EnableAddonRequest{}).
|
||||
Returns(200, "", apis.AddonStatusResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Param(ws.PathParameter("name", "addon name to update").DataType("string").Required(true)).
|
||||
Writes(apis.AddonStatusResponse{}))
|
||||
|
||||
return ws
|
||||
}
|
||||
|
||||
@@ -185,3 +202,69 @@ func (s *addonWebService) statusAddon(req *restful.Request, res *restful.Respons
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *addonWebService) updateAddon(req *restful.Request, res *restful.Response) {
|
||||
var createReq apis.EnableAddonRequest
|
||||
var args []byte
|
||||
_, err := req.Request.Body.Read(args)
|
||||
if err == nil {
|
||||
err := req.ReadEntity(&createReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err = validate.Struct(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
name := req.PathParameter("name")
|
||||
err = s.addonUsecase.UpdateAddon(req.Request.Context(), name, createReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
s.statusAddon(req, res)
|
||||
}
|
||||
|
||||
type enabledAddonWebService struct {
|
||||
addonUsecase usecase.AddonUsecase
|
||||
}
|
||||
|
||||
func (s *enabledAddonWebService) GetWebService() *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(versionPrefix+"/enabled-addon").
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML).
|
||||
Doc("api for addon management")
|
||||
|
||||
tags := []string{"addon"}
|
||||
|
||||
// List enabled addon from cluster
|
||||
ws.Route(ws.GET("/").To(s.list).
|
||||
Doc("list all addons").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("registry", "filter addons from given registry").DataType("string")).
|
||||
Param(ws.QueryParameter("query", "Fuzzy search based on name and description.").DataType("string")).
|
||||
Returns(200, "", apis.ListAddonResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.ListAddonResponse{}))
|
||||
|
||||
return ws
|
||||
}
|
||||
|
||||
func (s *enabledAddonWebService) list(req *restful.Request, res *restful.Response) {
|
||||
enabledAddons, err := s.addonUsecase.ListEnabledAddon(req.Request.Context())
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = res.WriteEntity(apis.ListEnabledAddonResponse{EnabledAddons: enabledAddons})
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ func Init(ds datastore.DataStore) {
|
||||
RegistWebService(NewNamespaceWebService(namespaceUsecase))
|
||||
RegistWebService(NewDefinitionWebservice(definitionUsecase))
|
||||
RegistWebService(NewAddonWebService(addonUsecase))
|
||||
RegistWebService(NewEnabledAddonWebService(addonUsecase))
|
||||
RegistWebService(NewAddonRegistryWebService(addonUsecase))
|
||||
RegistWebService(NewOAMApplication(oamApplicationUsecase))
|
||||
RegistWebService(&policyDefinitionWebservice{})
|
||||
|
||||
Reference in New Issue
Block a user