Skip to content

Commit 1a46a40

Browse files
feat: report tenant network policy enforcement
Signed-off-by: Shreyansh Sancheti <43677304+shreyanshjain7174@users.noreply.github.com>
1 parent 4f58389 commit 1a46a40

10 files changed

Lines changed: 280 additions & 16 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package v1alpha1
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
"testing"
7+
8+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
9+
"sigs.k8s.io/yaml"
10+
)
11+
12+
func TestTenantCompatibility_NetworkPolicyEnforcementReasonIsAdditive(t *testing.T) {
13+
legacy := []byte(`{"apiVersion":"agentic.clawdlinux.org/v1alpha1","kind":"Tenant","metadata":{"name":"legacy"},"spec":{"displayName":"Legacy","namespace":"tenant-a","providers":["openai"]},"status":{"networkPolicyActive":true}}`)
14+
var tenant Tenant
15+
if err := json.Unmarshal(legacy, &tenant); err != nil {
16+
t.Fatalf("unmarshal legacy Tenant: %v", err)
17+
}
18+
if tenant.Status.NetworkPolicyEnforcementReason != "" {
19+
t.Fatalf("legacy enforcement reason = %q, want empty", tenant.Status.NetworkPolicyEnforcementReason)
20+
}
21+
22+
current := []byte(`{"status":{"networkPolicyEnforcementReason":"Enforcing"}}`)
23+
if err := json.Unmarshal(current, &tenant); err != nil {
24+
t.Fatalf("unmarshal current Tenant status: %v", err)
25+
}
26+
if tenant.Status.NetworkPolicyEnforcementReason != "Enforcing" {
27+
t.Fatalf("enforcement reason = %q, want Enforcing", tenant.Status.NetworkPolicyEnforcementReason)
28+
}
29+
}
30+
31+
func TestTenantCompatibility_CRDNetworkPolicyEnforcementReason(t *testing.T) {
32+
data, err := os.ReadFile("../../config/crd/bases/agentic.clawdlinux.org_tenants.yaml")
33+
if err != nil {
34+
t.Fatalf("read Tenant CRD: %v", err)
35+
}
36+
var crd apiextensionsv1.CustomResourceDefinition
37+
if err := yaml.Unmarshal(data, &crd); err != nil {
38+
t.Fatalf("decode Tenant CRD: %v", err)
39+
}
40+
if len(crd.Spec.Versions) != 1 || crd.Spec.Versions[0].Schema == nil || crd.Spec.Versions[0].Schema.OpenAPIV3Schema == nil {
41+
t.Fatal("Tenant CRD has no v1alpha1 OpenAPI schema")
42+
}
43+
statusSchema, ok := crd.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["status"]
44+
if !ok {
45+
t.Fatal("Tenant CRD schema has no status property")
46+
}
47+
reasonSchema, ok := statusSchema.Properties["networkPolicyEnforcementReason"]
48+
if !ok || reasonSchema.Type != "string" {
49+
t.Fatalf("Tenant CRD status.networkPolicyEnforcementReason = %#v, want string", reasonSchema)
50+
}
51+
}

‎api/v1alpha1/tenant_types.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ type TenantStatus struct {
8585
// NetworkPolicyActive indicates if network policies are active
8686
NetworkPolicyActive bool `json:"networkPolicyActive,omitempty"`
8787

88+
// NetworkPolicyEnforcementReason describes passive CNI evidence. It does not
89+
// represent an active packet-flow verification.
90+
NetworkPolicyEnforcementReason string `json:"networkPolicyEnforcementReason,omitempty"`
91+
8892
// WorkloadCount is the number of active workloads for this tenant
8993
WorkloadCount int `json:"workloadCount,omitempty"`
9094

‎charts/templates/rbac.yaml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ rules:
3131
- configmaps
3232
- secrets
3333
- namespaces
34+
- resourcequotas
3435
- serviceaccounts
3536
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
3637
# Sandbox node readiness checks
@@ -64,16 +65,19 @@ rules:
6465
resources:
6566
- agentworkloads
6667
- agentcards
68+
- tenants
6769
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
6870
- apiGroups: ["agentic.clawdlinux.org"]
6971
resources:
7072
- agentworkloads/status
7173
- agentcards/status
74+
- tenants/status
7275
verbs: ["get", "update", "patch"]
7376
- apiGroups: ["agentic.clawdlinux.org"]
7477
resources:
7578
- agentworkloads/finalizers
7679
- agentcards/finalizers
80+
- tenants/finalizers
7781
verbs: ["update"]
7882
# RBAC (for sub-agent service accounts — namespace-scoped only)
7983
- apiGroups: ["rbac.authorization.k8s.io"]

‎charts/tests/operator_rbac_test.yaml‎

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ suite: operator controller RBAC
22
templates:
33
- templates/rbac.yaml
44
tests:
5-
- it: grants the running controllers access to AgentWorkload and AgentCard resources
5+
- it: grants the running controllers access to AgentWorkload, AgentCard, and Tenant resources
66
set:
77
license.key: dev-license
88
asserts:
@@ -14,6 +14,32 @@ tests:
1414
resources:
1515
- agentworkloads
1616
- agentcards
17+
- tenants
18+
verbs:
19+
- get
20+
- list
21+
- watch
22+
- create
23+
- update
24+
- patch
25+
- delete
26+
documentIndex: 1
27+
- contains:
28+
path: rules[0].resources
29+
content: resourcequotas
30+
documentIndex: 1
31+
- contains:
32+
path: rules[0].verbs
33+
content: watch
34+
documentIndex: 1
35+
- contains:
36+
path: rules
37+
content:
38+
apiGroups:
39+
- rbac.authorization.k8s.io
40+
resources:
41+
- roles
42+
- rolebindings
1743
verbs:
1844
- get
1945
- list
@@ -31,6 +57,7 @@ tests:
3157
resources:
3258
- agentworkloads/status
3359
- agentcards/status
60+
- tenants/status
3461
verbs:
3562
- get
3663
- update
@@ -44,6 +71,7 @@ tests:
4471
resources:
4572
- agentworkloads/finalizers
4673
- agentcards/finalizers
74+
- tenants/finalizers
4775
verbs:
4876
- update
4977
documentIndex: 1

‎cmd/main.go‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,16 @@ import (
3131
"github.com/prometheus/client_golang/prometheus"
3232
"github.com/prometheus/client_golang/prometheus/promhttp"
3333
corev1 "k8s.io/api/core/v1"
34+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435
"k8s.io/apimachinery/pkg/labels"
3536
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
3637
// to ensure that exec-entrypoint and run can make use of them.
3738
_ "k8s.io/client-go/plugin/pkg/client/auth"
3839

3940
"k8s.io/apimachinery/pkg/runtime"
4041
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
42+
"k8s.io/client-go/discovery"
43+
"k8s.io/client-go/kubernetes"
4144
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
4245
ctrl "sigs.k8s.io/controller-runtime"
4346
"sigs.k8s.io/controller-runtime/pkg/cache"
@@ -52,6 +55,7 @@ import (
5255
agenticv1alpha1 "github.com/Clawdlinux/agentic-operator-core/api/v1alpha1"
5356
runtimeadmission "github.com/Clawdlinux/agentic-operator-core/internal/admission"
5457
"github.com/Clawdlinux/agentic-operator-core/internal/controller"
58+
"github.com/Clawdlinux/agentic-operator-core/internal/netpolicy"
5559
"github.com/Clawdlinux/agentic-operator-core/pkg/evaluation"
5660
"github.com/Clawdlinux/agentic-operator-core/pkg/finops"
5761
"github.com/Clawdlinux/agentic-operator-core/pkg/governance"
@@ -289,6 +293,26 @@ func main() {
289293
os.Exit(1)
290294
}
291295

296+
discoveryClient, err := discovery.NewDiscoveryClientForConfig(ctrl.GetConfigOrDie())
297+
if err != nil {
298+
setupLog.Error(err, "Failed to create discovery client")
299+
os.Exit(1)
300+
}
301+
kubeClient, err := kubernetes.NewForConfig(ctrl.GetConfigOrDie())
302+
if err != nil {
303+
setupLog.Error(err, "Failed to create Kubernetes client")
304+
os.Exit(1)
305+
}
306+
if err := (&controller.TenantReconciler{
307+
Client: mgr.GetClient(),
308+
Scheme: mgr.GetScheme(),
309+
DiscoveryClient: netpolicy.DiscoveryClient(discoveryClient),
310+
DaemonSets: kubeClient.AppsV1().DaemonSets(metav1.NamespaceSystem),
311+
}).SetupWithManager(mgr); err != nil {
312+
setupLog.Error(err, "Failed to create controller", "controller", "Tenant")
313+
os.Exit(1)
314+
}
315+
292316
if os.Getenv("ENABLE_WEBHOOKS") != "false" {
293317
if err := (&agenticv1alpha1.AgentWorkload{}).SetupWebhookWithManager(mgr); err != nil {
294318
setupLog.Error(err, "Failed to setup webhook", "webhook", "AgentWorkload")

‎config/crd/bases/agentic.clawdlinux.org_tenants.yaml‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
33
kind: CustomResourceDefinition
44
metadata:
55
annotations:
6-
controller-gen.kubebuilder.io/version: v0.20.1
6+
controller-gen.kubebuilder.io/version: v0.22.0
77
name: tenants.agentic.clawdlinux.org
88
spec:
99
group: agentic.clawdlinux.org
@@ -175,6 +175,11 @@ spec:
175175
description: NetworkPolicyActive indicates if network policies are
176176
active
177177
type: boolean
178+
networkPolicyEnforcementReason:
179+
description: |-
180+
NetworkPolicyEnforcementReason describes passive CNI evidence. It does not
181+
represent an active packet-flow verification.
182+
type: string
178183
phase:
179184
description: Phase is the current provisioning phase
180185
enum:

‎config/rbac/role.yaml‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ rules:
2727
- ""
2828
resources:
2929
- resourcequotas
30-
- serviceaccounts
3130
verbs:
3231
- create
3332
- get
3433
- list
3534
- patch
35+
- watch
3636
- apiGroups:
3737
- ""
3838
resources:
@@ -42,6 +42,15 @@ rules:
4242
- get
4343
- list
4444
- watch
45+
- apiGroups:
46+
- ""
47+
resources:
48+
- serviceaccounts
49+
verbs:
50+
- create
51+
- get
52+
- list
53+
- patch
4554
- apiGroups:
4655
- agentic.clawdlinux.org
4756
resources:
@@ -75,14 +84,11 @@ rules:
7584
- patch
7685
- update
7786
- apiGroups:
78-
- networking.k8s.io
87+
- apps
7988
resources:
80-
- networkpolicies
89+
- daemonsets
8190
verbs:
82-
- create
83-
- get
8491
- list
85-
- patch
8692
- apiGroups:
8793
- rbac.authorization.k8s.io
8894
resources:
@@ -93,3 +99,4 @@ rules:
9399
- get
94100
- list
95101
- patch
102+
- watch

0 commit comments

Comments
 (0)