Skip to content

Commit

Permalink
Merge pull request #220 from grycap/devel
Browse files Browse the repository at this point in the history
Added parameter VO to FDL
  • Loading branch information
catttam authored Nov 29, 2023
2 parents cb6abfc + fc3d9ce commit 921cb86
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/fdl.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ storage_providers:
| `memory` </br> *string* | Memory limit for the service following the [kubernetes format](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory). Optional (default: 256Mi) |
| `cpu` </br> *string* | CPU limit for the service following the [kubernetes format](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu). Optional (default: 0.2) |
| `enable_gpu` </br> *bool* | Parameter to enable the use of GPU for the service. Requires a device plugin deployed on the cluster (More info: [Kubernetes device plugins](https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/#using-device-plugins)). Optional (default: false) |
| `enable_sgx` </br> *bool* | Parameter to enable the use of SGX plugin on the cluster containers. (More info: [SGX plugin documentation](https://sconedocs.github.io/helm_sgxdevplugin/)). Optional (default: false) |
| `image_prefetch` </br> *bool* | Parameter to enable the use of image caching. Optional (default: false) |
| `total_memory` </br> *string* | Limit for the memory used by all the service's jobs running simultaneously. Apache YuniKorn scheduler is required to work. Same format as Memory, but internally translated to MB (integer). Optional (default: "") |
| `total_cpu` </br> *string* | Limit for the virtual CPUs used by all the service's jobs running simultaneously. Apache YuniKorn scheduler is required to work. Same format as CPU, but internally translated to millicores (integer). Optional (default: "") |
Expand Down
8 changes: 8 additions & 0 deletions pkg/backends/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ func (k *KubeBackend) CreateService(service types.Service) error {
return err
}

if service.VO != "" {
for _, vo := range k.config.OIDCGroups {
if vo == service.VO {
service.Labels["vo"] = service.VO
}
}
}

// Create podSpec from the service
podSpec, err := service.ToPodSpec(k.config)
if err != nil {
Expand Down
16 changes: 16 additions & 0 deletions pkg/backends/knative.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,15 @@ func (kn *KnativeBackend) createKNServiceDefinition(service *types.Service) (*kn
// https://knative.dev/docs/serving/services/private-services/
service.Labels[types.KnativeVisibilityLabel] = types.KnativeClusterLocalValue

// Add to the service labels the user VO for accounting on k8s pods
if service.VO != "" {
for _, vo := range kn.config.OIDCGroups {
if vo == service.VO {
service.Labels["vo"] = service.VO
}
}
}

podSpec, err := service.ToPodSpec(kn.config)
if err != nil {
return nil, err
Expand All @@ -302,6 +311,8 @@ func (kn *KnativeBackend) createKNServiceDefinition(service *types.Service) (*kn
types.KnativeMinScaleAnnotation: strconv.Itoa(service.Synchronous.MinScale),
types.KnativeMaxScaleAnnotation: strconv.Itoa(service.Synchronous.MaxScale),
},
//Empty labels map to avoid nil pointer errors
Labels: map[string]string{},
},
Spec: knv1.RevisionSpec{
ContainerConcurrency: &containerConcurrency,
Expand All @@ -312,6 +323,11 @@ func (kn *KnativeBackend) createKNServiceDefinition(service *types.Service) (*kn
},
}

// Add to the service labels the user VO for accounting on knative pods
if service.Labels["vo"] != "" {
knSvc.Spec.ConfigurationSpec.Template.ObjectMeta.Labels["vo"] = service.Labels["vo"]
}

if service.EnableSGX {
knSvc.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations["kubernetes.podspec-securitycontext"] = "enabled"
knSvc.Spec.ConfigurationSpec.Template.ObjectMeta.Annotations["kubernetes.containerspec-addcapabilities"] = "enabled"
Expand Down
24 changes: 24 additions & 0 deletions pkg/handlers/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/grycap/cdmi-client-go"
"github.com/grycap/oscar/v2/pkg/types"
"github.com/grycap/oscar/v2/pkg/utils"
"github.com/grycap/oscar/v2/pkg/utils/auth"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
)

Expand All @@ -46,6 +47,7 @@ var errInput = errors.New("unrecognized input (valid inputs are MinIO and dCache
func MakeCreateHandler(cfg *types.Config, back types.ServerlessBackend) gin.HandlerFunc {
return func(c *gin.Context) {
var service types.Service

if err := c.ShouldBindJSON(&service); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("The service specification is not valid: %v", err))
return
Expand All @@ -54,6 +56,24 @@ func MakeCreateHandler(cfg *types.Config, back types.ServerlessBackend) gin.Hand
// Check service values and set defaults
checkValues(&service, cfg)

if service.VO != "" {
oidcManager, _ := auth.NewOIDCManager(cfg.OIDCIssuer, cfg.OIDCSubject, cfg.OIDCGroups)

authHeader := c.GetHeader("Authorization")
rawToken := strings.TrimPrefix(authHeader, "Bearer ")
hasVO, err2 := oidcManager.UserHasVO(rawToken, service.VO)

if err2 != nil {
c.String(http.StatusInternalServerError, err2.Error())
return
}

if !hasVO {
c.String(http.StatusBadRequest, fmt.Sprintf("This user isn't enrrolled on the vo: %v", service.VO))
return
}
}

// Create the service
if err := back.CreateService(service); err != nil {
// Check if error is caused because the service name provided already exists
Expand Down Expand Up @@ -120,6 +140,10 @@ func checkValues(service *types.Service, cfg *types.Config) {
service.Labels[types.YunikornApplicationIDLabel] = service.Name
service.Labels[types.YunikornQueueLabel] = fmt.Sprintf("%s.%s.%s", types.YunikornRootQueue, types.YunikornOscarQueue, service.Name)

if service.VO != "" {
service.Labels["vo"] = service.VO
}

// Create default annotations map
if service.Annotations == nil {
service.Annotations = make(map[string]string)
Expand Down
4 changes: 4 additions & 0 deletions pkg/types/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ type Service struct {
// Optional
Annotations map[string]string `json:"annotations"`

// Parameter to specify the VO from the user creating the service
// Optional
VO string `json:"vo"`

// Labels user-defined Kubernetes labels to be set in job's definition
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
// Optional
Expand Down
1 change: 1 addition & 0 deletions pkg/types/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ environment:
TEST_VAR: testvalue
annotations:
testannotation: testannotationvalue
vo: ""
labels:
testlabel: testlabelvalue
storage_providers:
Expand Down
17 changes: 15 additions & 2 deletions pkg/utils/auth/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type userInfo struct {
}

// newOIDCManager returns a new oidcManager or error if the oidc.Provider can't be created
func newOIDCManager(issuer string, subject string, groups []string) (*oidcManager, error) {
func NewOIDCManager(issuer string, subject string, groups []string) (*oidcManager, error) {
provider, err := oidc.NewProvider(context.TODO(), issuer)
if err != nil {
return nil, err
Expand All @@ -66,7 +66,7 @@ func newOIDCManager(issuer string, subject string, groups []string) (*oidcManage

// getIODCMiddleware returns the Gin's handler middleware to validate OIDC-based auth
func getOIDCMiddleware(issuer string, subject string, groups []string) gin.HandlerFunc {
oidcManager, err := newOIDCManager(issuer, subject, groups)
oidcManager, err := NewOIDCManager(issuer, subject, groups)
if err != nil {
return func(c *gin.Context) {
c.AbortWithStatus(http.StatusUnauthorized)
Expand Down Expand Up @@ -140,6 +140,19 @@ func getGroups(urns []string) []string {
return groups
}

func (om *oidcManager) UserHasVO(rawToken string, vo string) (bool, error) {
ui, err := om.getUserInfo(rawToken)
if err != nil {
return false, err
}
for _, gr := range ui.groups {
if vo == gr {
return true, nil
}
}
return false, nil
}

// isAuthorised checks if a token is authorised to access the API
func (om *oidcManager) isAuthorised(rawToken string) bool {
// Check if the token is valid
Expand Down

0 comments on commit 921cb86

Please sign in to comment.