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:
wyike
2021-12-01 10:27:26 +08:00
committed by GitHub
parent 4294cc8a98
commit 163415479b
5 changed files with 194 additions and 40 deletions
+13 -15
View File
@@ -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
}
+6
View File
@@ -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"`
+91 -25
View File
@@ -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
}
}
+83
View File
@@ -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{})