@@ -32,6 +32,7 @@ const (
3232 base1ns = "base1ns"
3333 base1ns6didler = "base1ns6didler"
3434 base1nsnoidling = "base1nsnoidling"
35+ claw = "claw"
3536
3637 // common CPU limits
3738 baseCPULimit = "40000m"
@@ -62,6 +63,8 @@ func NewChecksForTier(tier *toolchainv1alpha1.NSTemplateTier) (TierChecks, error
6263 return & appstudiolargeTierChecks {appstudioTierChecks {tierName : appstudiolarge }}, nil
6364 case appstudioEnv :
6465 return & appstudioEnvTierChecks {tierName : appstudioEnv }, nil
66+ case claw :
67+ return & clawTierChecks {tierName : claw }, nil
6568 default :
6669 return nil , fmt .Errorf ("no assertion implementation found for %s" , tier .Name )
6770 }
@@ -113,6 +116,7 @@ func (a *baseTierChecks) GetNamespaceObjectChecks(nsType string) []namespaceObje
113116 checks := []namespaceObjectsCheck {
114117 numberOfLimitRanges (1 ),
115118 limitRange ("1" , "1Gi" , "10m" , "64Mi" ),
119+ resourceQuotaSpaceRequests (),
116120 execPodsRole (),
117121 crtadminPodsRoleBinding (),
118122 crtadminViewRoleBinding (),
@@ -197,6 +201,7 @@ func (a *base1nsTierChecks) GetNamespaceObjectChecks(_ string) []namespaceObject
197201 corev1 .ResourceName ("limits.nvidia.com/gpu" ): "0" ,
198202 }),
199203 resourceQuotaStorage ("15Gi" , "80Gi" , "15Gi" , "10" ),
204+ resourceQuotaSpaceRequests (),
200205 limitRange ("1" , "1000Mi" , "10m" , "64Mi" ),
201206 numberOfLimitRanges (1 ),
202207 execPodsRole (),
@@ -525,6 +530,171 @@ func (a *appstudioEnvTierChecks) GetClusterObjectChecks() []clusterObjectsCheck
525530 idlers (0 , "env" ))
526531}
527532
533+ type clawTierChecks struct {
534+ tierName string
535+ }
536+
537+ func (a * clawTierChecks ) GetNamespaceObjectChecks (_ string ) []namespaceObjectsCheck {
538+ checks := []namespaceObjectsCheck {
539+ resourceQuotaComputeDeployNoScope ("8" , "10Gi" , "1" , "3Gi" ),
540+ resourceQuotaStorage ("5Gi" , "15Gi" , "5Gi" , "1" ),
541+ limitRange ("500m" , "512Mi" , "10m" , "64Mi" ),
542+ numberOfLimitRanges (1 ),
543+ execPodsRole (),
544+ crtadminPodsRoleBinding (),
545+ crtadminViewRoleBinding (),
546+ networkPolicySameNamespace (),
547+ networkPolicyAllowFromIngress (),
548+ networkPolicyAllowFromMonitoring (),
549+ networkPolicyAllowFromOlmNamespaces (),
550+ networkPolicyAllowFromConsoleNamespaces (),
551+ numberOfNetworkPolicies (5 ),
552+ }
553+ return checks
554+ }
555+
556+ func (a * clawTierChecks ) GetSpaceRoleChecks (spaceRoles map [string ][]string ) ([]spaceRoleObjectsCheck , error ) {
557+ checks := []spaceRoleObjectsCheck {}
558+ roles := 0
559+ rolebindings := 0
560+ for role , usernames := range spaceRoles {
561+ switch role {
562+ case "admin" :
563+ checks = append (checks , clawUserRole ())
564+ roles ++
565+ for _ , userName := range usernames {
566+ checks = append (checks ,
567+ clawUserRoleBinding (userName ),
568+ clawViewRoleBinding (userName ),
569+ )
570+ rolebindings += 2
571+ }
572+ default :
573+ return nil , fmt .Errorf ("unexpected template name: '%s'" , role )
574+ }
575+ }
576+ checks = append (checks ,
577+ numberOfToolchainRoles (roles + 1 ), // +1 for `exec-pods`
578+ numberOfToolchainRoleBindings (rolebindings + 2 ), // +2 for `crtadmin-pods` and `crtadmin-view`
579+ )
580+ return checks , nil
581+ }
582+
583+ func (a * clawTierChecks ) GetExpectedTemplateRefs (t * testing.T , hostAwait * wait.HostAwaitility ) TemplateRefs {
584+ templateRefs := GetTemplateRefs (t , hostAwait , a .tierName )
585+ verifyNsTypes (t , a .tierName , templateRefs , "claw" )
586+ return templateRefs
587+ }
588+
589+ func (a * clawTierChecks ) GetClusterObjectChecks () []clusterObjectsCheck {
590+ return clusterObjectsChecks (
591+ clusterResourceQuotaClaw (),
592+ numberOfClusterResourceQuotas (1 ),
593+ idlers (43200 , "claw" ))
594+ }
595+
596+ func clusterResourceQuotaClaw () clusterObjectsCheckCreator {
597+ return func () clusterObjectsCheck {
598+ return func (t * testing.T , memberAwait * wait.MemberAwaitility , userName , tierLabel string ) {
599+ var err error
600+ hard := make (map [corev1.ResourceName ]resource.Quantity )
601+ hard [count ("deployments.apps" )], err = resource .ParseQuantity ("5" )
602+ require .NoError (t , err )
603+ hard [count (corev1 .ResourcePods )], err = resource .ParseQuantity ("10" )
604+ require .NoError (t , err )
605+ hard [count ("routes.route.openshift.io" )], err = resource .ParseQuantity ("3" )
606+ require .NoError (t , err )
607+ hard [count (corev1 .ResourceServices )], err = resource .ParseQuantity ("5" )
608+ require .NoError (t , err )
609+ hard [count (corev1 .ResourceSecrets )], err = resource .ParseQuantity ("50" )
610+ require .NoError (t , err )
611+ hard [count (corev1 .ResourceConfigMaps )], err = resource .ParseQuantity ("10" )
612+ require .NoError (t , err )
613+
614+ _ , err = memberAwait .WaitForClusterResourceQuota (t , fmt .Sprintf ("for-%s-claw" , userName ),
615+ crqToolchainLabelsWaitCriterion (userName ),
616+ clusterResourceQuotaMatches (userName , tierLabel , hard ),
617+ )
618+ require .NoError (t , err )
619+ }
620+ }
621+ }
622+
623+ func resourceQuotaComputeDeployNoScope (cpuLimit , memoryLimit , cpuRequest , memoryRequest string ) namespaceObjectsCheck {
624+ return func (t * testing.T , ns * corev1.Namespace , memberAwait * wait.MemberAwaitility , _ string ) {
625+ var err error
626+ spec := corev1.ResourceQuotaSpec {
627+ Hard : make (map [corev1.ResourceName ]resource.Quantity ),
628+ }
629+ spec .Hard [corev1 .ResourceLimitsCPU ], err = resource .ParseQuantity (cpuLimit )
630+ require .NoError (t , err )
631+ spec .Hard [corev1 .ResourceLimitsMemory ], err = resource .ParseQuantity (memoryLimit )
632+ require .NoError (t , err )
633+ spec .Hard [corev1 .ResourceRequestsCPU ], err = resource .ParseQuantity (cpuRequest )
634+ require .NoError (t , err )
635+ spec .Hard [corev1 .ResourceRequestsMemory ], err = resource .ParseQuantity (memoryRequest )
636+ require .NoError (t , err )
637+
638+ criteria := resourceQuotaMatches (ns .Name , "compute-deploy" , spec )
639+ _ , err = memberAwait .WaitForResourceQuota (t , ns .Name , "compute-deploy" , criteria )
640+ require .NoError (t , err )
641+ }
642+ }
643+
644+ func clawUserRole () spaceRoleObjectsCheck {
645+ return func (t * testing.T , ns * corev1.Namespace , memberAwait * wait.MemberAwaitility , owner string ) {
646+ role , err := memberAwait .WaitForRole (t , ns , "claw-user" , toolchainLabelsWaitCriterion (owner )... )
647+ require .NoError (t , err )
648+ expected := & rbacv1.Role {
649+ Rules : []rbacv1.PolicyRule {
650+ {
651+ APIGroups : []string {"claw.sandbox.redhat.com" },
652+ Resources : []string {"claws" },
653+ Verbs : []string {"get" , "list" , "watch" , "create" , "update" , "patch" , "delete" },
654+ },
655+ {
656+ APIGroups : []string {"" },
657+ Resources : []string {"pods/exec" },
658+ Verbs : []string {"get" , "create" },
659+ },
660+ {
661+ APIGroups : []string {"" },
662+ Resources : []string {"secrets" },
663+ Verbs : []string {"get" , "list" , "watch" , "create" , "update" , "patch" , "delete" },
664+ },
665+ },
666+ }
667+ assert .Len (t , role .Rules , len (expected .Rules ))
668+ assert .Equal (t , expected .Rules , role .Rules )
669+ }
670+ }
671+
672+ func clawUserRoleBinding (userName string ) spaceRoleObjectsCheck {
673+ return func (t * testing.T , ns * corev1.Namespace , memberAwait * wait.MemberAwaitility , owner string ) {
674+ rb , err := memberAwait .WaitForRoleBinding (t , ns , userName + "-claw-user" , toolchainLabelsWaitCriterion (owner )... )
675+ require .NoError (t , err )
676+ assert .Len (t , rb .Subjects , 1 )
677+ assert .Equal (t , "User" , rb .Subjects [0 ].Kind )
678+ assert .Equal (t , userName , rb .Subjects [0 ].Name )
679+ assert .Equal (t , "claw-user" , rb .RoleRef .Name )
680+ assert .Equal (t , "Role" , rb .RoleRef .Kind )
681+ assert .Equal (t , "rbac.authorization.k8s.io" , rb .RoleRef .APIGroup )
682+ }
683+ }
684+
685+ func clawViewRoleBinding (userName string ) spaceRoleObjectsCheck {
686+ return func (t * testing.T , ns * corev1.Namespace , memberAwait * wait.MemberAwaitility , owner string ) {
687+ rb , err := memberAwait .WaitForRoleBinding (t , ns , userName + "-view" , toolchainLabelsWaitCriterion (owner )... )
688+ require .NoError (t , err )
689+ assert .Len (t , rb .Subjects , 1 )
690+ assert .Equal (t , "User" , rb .Subjects [0 ].Kind )
691+ assert .Equal (t , userName , rb .Subjects [0 ].Name )
692+ assert .Equal (t , "view" , rb .RoleRef .Name )
693+ assert .Equal (t , "ClusterRole" , rb .RoleRef .Kind )
694+ assert .Equal (t , "rbac.authorization.k8s.io" , rb .RoleRef .APIGroup )
695+ }
696+ }
697+
528698// verifyNsTypes checks that there's a namespace.TemplateRef that begins with `<tier>-<type>` for each given templateRef (and no more, no less)
529699func verifyNsTypes (t * testing.T , tier string , templateRefs TemplateRefs , expectedNSTypes ... string ) {
530700 require .Len (t , templateRefs .Namespaces , len (expectedNSTypes ))
@@ -728,6 +898,21 @@ func resourceQuotaStorage(ephemeralLimit, storageRequest, ephemeralRequest, pvcs
728898 }
729899}
730900
901+ func resourceQuotaSpaceRequests () namespaceObjectsCheck {
902+ return func (t * testing.T , ns * corev1.Namespace , memberAwait * wait.MemberAwaitility , _ string ) {
903+ var err error
904+ spec := corev1.ResourceQuotaSpec {
905+ Hard : make (map [corev1.ResourceName ]resource.Quantity ),
906+ }
907+ spec .Hard ["count/spacerequests.toolchain.dev.openshift.com" ], err = resource .ParseQuantity ("1" )
908+ require .NoError (t , err )
909+
910+ criteria := resourceQuotaMatches (ns .Name , "compute-spacerequests" , spec )
911+ _ , err = memberAwait .WaitForResourceQuota (t , ns .Name , "compute-spacerequests" , criteria )
912+ require .NoError (t , err )
913+ }
914+ }
915+
731916func resourceQuotaToolchainCrds (spaceRequestLimit string ) namespaceObjectsCheck {
732917 return func (t * testing.T , ns * corev1.Namespace , memberAwait * wait.MemberAwaitility , _ string ) {
733918 var err error
0 commit comments