Support manual triggering of a repository update. (#6414)
* Support manual triggering of a repository update. * cherry pick add api for workload template (#1982) * cherry pick (add operator application (#1970)) * Modify routing implementation to improve readability * cherry pick from kse dfc40e5adf5aa2e67d1 * Filter by Routing Parameter Namespace (#1990) * add doc for workloadtemplates --------- Co-authored-by: inksnw <inksnw@gmail.com>
This commit is contained in:
committed by
GitHub
parent
c9c856dfda
commit
bb60d39434
203
pkg/kapis/workloadtemplate/v1alpha1/handler.go
Normal file
203
pkg/kapis/workloadtemplate/v1alpha1/handler.go
Normal file
@@ -0,0 +1,203 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
tenantv1beta1 "kubesphere.io/api/tenant/v1beta1"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/request"
|
||||
"kubesphere.io/kubesphere/pkg/utils/stringutils"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||
k8suitl "kubesphere.io/kubesphere/pkg/utils/k8sutil"
|
||||
)
|
||||
|
||||
func (h *templateHandler) listWorkloadTemplate(req *restful.Request, resp *restful.Response) {
|
||||
secretList := corev1.SecretList{}
|
||||
requirements, _ := labels.SelectorFromSet(map[string]string{SchemeGroupVersion.Group: "true"}).Requirements()
|
||||
userSelector := query.ParseQueryParameter(req).Selector()
|
||||
combinedSelector := labels.NewSelector()
|
||||
combinedSelector = combinedSelector.Add(requirements...)
|
||||
if userSelector != nil {
|
||||
userRequirements, _ := userSelector.Requirements()
|
||||
combinedSelector = combinedSelector.Add(userRequirements...)
|
||||
}
|
||||
opts := []client.ListOption{
|
||||
client.MatchingLabelsSelector{Selector: combinedSelector},
|
||||
}
|
||||
namespace := req.PathParameter("namespace")
|
||||
if namespace != "" {
|
||||
opts = append(opts, client.InNamespace(namespace))
|
||||
}
|
||||
err := h.client.List(req.Request.Context(), &secretList, opts...)
|
||||
if err != nil {
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
workspace := req.PathParameter("workspace")
|
||||
if workspace == "" {
|
||||
resp.WriteEntity(k8suitl.ConvertToListResult(&secretList, req))
|
||||
return
|
||||
}
|
||||
|
||||
user, ok := request.UserFrom(req.Request.Context())
|
||||
if !ok {
|
||||
err := fmt.Errorf("cannot obtain user info")
|
||||
klog.Errorln(err)
|
||||
api.HandleForbidden(resp, nil, err)
|
||||
return
|
||||
}
|
||||
|
||||
filteredList, err := h.FilterByPermissions(workspace, user, secretList)
|
||||
if err != nil {
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp.WriteEntity(k8suitl.ConvertToListResult(filteredList, req))
|
||||
}
|
||||
|
||||
func (h *templateHandler) FilterByPermissions(workspace string, user user.Info, secretList corev1.SecretList) (*corev1.SecretList, error) {
|
||||
|
||||
listNS := authorizer.AttributesRecord{
|
||||
User: user,
|
||||
Verb: "list",
|
||||
Workspace: workspace,
|
||||
Resource: "namespaces",
|
||||
ResourceRequest: true,
|
||||
ResourceScope: request.WorkspaceScope,
|
||||
}
|
||||
|
||||
decision, _, err := h.authorizer.Authorize(listNS)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
var namespaceList []string
|
||||
if decision == authorizer.DecisionAllow {
|
||||
queryParam := query.New()
|
||||
queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s=%s", tenantv1beta1.WorkspaceLabel, workspace))
|
||||
result, err := h.resourceGetter.List("namespaces", "", queryParam)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range result.Items {
|
||||
ns := item.(*corev1.Namespace)
|
||||
listWorkLoadTemplate := authorizer.AttributesRecord{
|
||||
User: user,
|
||||
Verb: "list",
|
||||
Namespace: ns.Name,
|
||||
Resource: "workloadtemplates",
|
||||
ResourceRequest: true,
|
||||
ResourceScope: request.NamespaceScope,
|
||||
}
|
||||
decision, _, err = h.authorizer.Authorize(listWorkLoadTemplate)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
if decision == authorizer.DecisionAllow {
|
||||
namespaceList = append(namespaceList, ns.Name)
|
||||
} else {
|
||||
klog.Infof("user %s has no permission to list workloadtemplate in namespace %s", user.GetName(), ns.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filteredList := &corev1.SecretList{}
|
||||
for _, item := range secretList.Items {
|
||||
if !stringutils.StringIn(item.Namespace, namespaceList) {
|
||||
continue
|
||||
}
|
||||
filteredList.Items = append(filteredList.Items, item)
|
||||
}
|
||||
return filteredList, nil
|
||||
}
|
||||
|
||||
func (h *templateHandler) applyWorkloadTemplate(req *restful.Request, resp *restful.Response) {
|
||||
secret := &corev1.Secret{}
|
||||
err := req.ReadEntity(secret)
|
||||
if err != nil {
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
if req.PathParameter("workloadtemplate") == "" {
|
||||
// create new
|
||||
ns := req.PathParameter("namespace")
|
||||
err = h.client.Get(req.Request.Context(), runtimeclient.ObjectKey{Name: secret.Name, Namespace: ns}, secret)
|
||||
if err != nil && !apierrors.IsNotFound(err) {
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
api.HandleConflict(resp, req, fmt.Errorf("workloadtemplate %s already exists", secret.Name))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
newSecret := &corev1.Secret{}
|
||||
newSecret.Name = secret.Name
|
||||
newSecret.Namespace = req.PathParameter("namespace")
|
||||
mutateFn := func() error {
|
||||
newSecret.Annotations = secret.Annotations
|
||||
if secret.Labels == nil {
|
||||
secret.Labels = make(map[string]string)
|
||||
}
|
||||
newSecret.Labels = secret.Labels
|
||||
newSecret.Labels[SchemeGroupVersion.Group] = "true"
|
||||
newSecret.StringData = secret.StringData
|
||||
newSecret.Type = corev1.SecretType(fmt.Sprintf("%s/%s", SchemeGroupVersion.Group, "workloadtemplate"))
|
||||
return nil
|
||||
}
|
||||
_, err = controllerutil.CreateOrUpdate(req.Request.Context(), h.client, newSecret, mutateFn)
|
||||
if err != nil {
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
newSecret.SetManagedFields(nil)
|
||||
resp.WriteAsJson(newSecret)
|
||||
}
|
||||
|
||||
func (h *templateHandler) deleteWorkloadTemplate(req *restful.Request, resp *restful.Response) {
|
||||
name := req.PathParameter("workloadtemplate")
|
||||
secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: name}}
|
||||
secret.Namespace = req.PathParameter("namespace")
|
||||
err := h.client.Delete(req.Request.Context(), secret)
|
||||
if err != nil {
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
resp.WriteEntity(errors.None)
|
||||
}
|
||||
|
||||
func (h *templateHandler) getWorkloadTemplate(req *restful.Request, resp *restful.Response) {
|
||||
name := req.PathParameter("workloadtemplate")
|
||||
secret := &corev1.Secret{}
|
||||
ns := req.PathParameter("namespace")
|
||||
err := h.client.Get(req.Request.Context(), runtimeclient.ObjectKey{Name: name, Namespace: ns}, secret)
|
||||
if err != nil {
|
||||
api.HandleError(resp, req, err)
|
||||
return
|
||||
}
|
||||
secret.SetManagedFields(nil)
|
||||
resp.WriteAsJson(secret)
|
||||
}
|
||||
89
pkg/kapis/workloadtemplate/v1alpha1/register.go
Normal file
89
pkg/kapis/workloadtemplate/v1alpha1/register.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/Masterminds/semver/v3"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
|
||||
resourcesv1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/rest"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/runtime"
|
||||
)
|
||||
|
||||
var (
|
||||
SchemeGroupVersion = schema.GroupVersion{Group: "workloadtemplate.kubesphere.io", Version: "v1alpha1"}
|
||||
)
|
||||
|
||||
type templateHandler struct {
|
||||
client runtimeclient.Client
|
||||
authorizer authorizer.Authorizer
|
||||
resourceGetter *resourcesv1alpha3.Getter
|
||||
}
|
||||
|
||||
func NewHandler(cacheClient runtimeclient.Client, k8sVersion *semver.Version, authorizer authorizer.Authorizer) rest.Handler {
|
||||
handler := &templateHandler{
|
||||
client: cacheClient,
|
||||
authorizer: authorizer,
|
||||
resourceGetter: resourcesv1alpha3.NewResourceGetter(cacheClient, k8sVersion),
|
||||
}
|
||||
return handler
|
||||
}
|
||||
func (h *templateHandler) AddToContainer(c *restful.Container) (err error) {
|
||||
ws := runtime.NewWebService(SchemeGroupVersion)
|
||||
|
||||
ws.Route(ws.GET("/workloadtemplates").
|
||||
To(h.listWorkloadTemplate).
|
||||
Doc("List workload templates").
|
||||
Notes("List workload templates.").
|
||||
Operation("listWorkloadTemplate"))
|
||||
ws.Route(ws.GET("/workspaces/{workspace}/workloadtemplates").
|
||||
To(h.listWorkloadTemplate).
|
||||
Doc("List workload templates in a workspace").
|
||||
Notes("List workload templates in a workspace.").
|
||||
Operation("listWorkloadTemplate").
|
||||
Param(ws.PathParameter("workspace", "workspace")))
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/workloadtemplates").
|
||||
To(h.listWorkloadTemplate).
|
||||
Doc("List workload templates in a namespace").
|
||||
Notes("List workload templates in a namespace.").
|
||||
Operation("listWorkloadTemplate").
|
||||
Param(ws.PathParameter("namespace", "namespace")))
|
||||
|
||||
ws.Route(ws.POST("/namespaces/{namespace}/workloadtemplates").
|
||||
To(h.applyWorkloadTemplate).
|
||||
Doc("Apply a workload template in a namespace").
|
||||
Notes("Apply a workload template in a namespace.").
|
||||
Operation("applyWorkloadTemplate").
|
||||
Param(ws.PathParameter("namespace", "namespace")))
|
||||
|
||||
ws.Route(ws.PUT("/namespaces/{namespace}/workloadtemplates/{workloadtemplate}").
|
||||
To(h.applyWorkloadTemplate).
|
||||
Doc("Update a workload template").
|
||||
Notes("Update a workload template in a namespace.").
|
||||
Operation("applyWorkloadTemplate").
|
||||
Param(ws.PathParameter("namespace", "namespace")).
|
||||
Param(ws.PathParameter("workloadtemplate", "workloadtemplate")))
|
||||
|
||||
ws.Route(ws.DELETE("/namespaces/{namespace}/workloadtemplates/{workloadtemplate}").
|
||||
To(h.deleteWorkloadTemplate).
|
||||
Doc("Delete a workload template in a namespace").
|
||||
Notes("List workload templates in a namespace.").
|
||||
Operation("deleteWorkloadTemplate").
|
||||
Param(ws.PathParameter("namespace", "namespace")).
|
||||
Param(ws.PathParameter("workloadtemplate", "workloadtemplate")))
|
||||
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/workloadtemplates/{workloadtemplate}").
|
||||
To(h.getWorkloadTemplate).
|
||||
Doc("Get a workload template in a namespace").
|
||||
Notes("Get a workload template in a namespace.").
|
||||
Operation("getWorkloadTemplate").
|
||||
Param(ws.PathParameter("namespace", "namespace")).
|
||||
Param(ws.PathParameter("workloadtemplate", "workloadtemplate")))
|
||||
|
||||
c.Add(ws)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user