Files
kubesphere/pkg/kapis/workloadtemplate/v1alpha1/handler.go
2024-11-26 10:17:36 +08:00

209 lines
6.3 KiB
Go

/*
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
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)
}