Skip to content

Commit 7225640

Browse files
committed
Webhook validation for networks .Spec.NetworkNamespace field
Signed-off-by: Andrea Panattoni <[email protected]>
1 parent 996033c commit 7225640

File tree

4 files changed

+193
-0
lines changed

4 files changed

+193
-0
lines changed

bindata/manifests/operator-webhook/003-webhook.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,15 @@ webhooks:
6969
apiGroups: [ "sriovnetwork.openshift.io" ]
7070
apiVersions: [ "v1" ]
7171
resources: [ "sriovnetworkpoolconfigs" ]
72+
- operations: [ "CREATE", "UPDATE", ]
73+
apiGroups: [ "sriovnetwork.openshift.io" ]
74+
apiVersions: [ "v1" ]
75+
resources: [ "sriovnetworks" ]
76+
- operations: [ "CREATE", "UPDATE", ]
77+
apiGroups: [ "sriovnetwork.openshift.io" ]
78+
apiVersions: [ "v1" ]
79+
resources: [ "sriovibnetworks" ]
80+
- operations: [ "CREATE", "UPDATE", ]
81+
apiGroups: [ "sriovnetwork.openshift.io" ]
82+
apiVersions: [ "v1" ]
83+
resources: [ "ovsnetworks" ]

pkg/webhook/validate_networks.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package webhook
2+
3+
import (
4+
"fmt"
5+
6+
v1 "k8s.io/api/admission/v1"
7+
8+
sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
9+
"github.com/k8snetworkplumbingwg/sriov-network-operator/controllers"
10+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
11+
)
12+
13+
func validateSriovNetwork(cr *sriovnetworkv1.SriovNetwork, operation v1.Operation) (bool, []string, error) {
14+
err := validateNetworkNamespace(cr)
15+
if err != nil {
16+
return false, nil, err
17+
}
18+
return true, nil, nil
19+
}
20+
21+
func validateSriovIBNetwork(cr *sriovnetworkv1.SriovIBNetwork, operation v1.Operation) (bool, []string, error) {
22+
err := validateNetworkNamespace(cr)
23+
if err != nil {
24+
return false, nil, err
25+
}
26+
return true, nil, nil
27+
}
28+
29+
func validateOVSNetwork(cr *sriovnetworkv1.OVSNetwork, operation v1.Operation) (bool, []string, error) {
30+
err := validateNetworkNamespace(cr)
31+
if err != nil {
32+
return false, nil, err
33+
}
34+
return true, nil, nil
35+
}
36+
37+
func validateNetworkNamespace(cr controllers.NetworkCRInstance) error {
38+
if cr.GetNamespace() != vars.Namespace && cr.NetworkNamespace() != "" {
39+
return fmt.Errorf(".Spec.NetworkNamespace field can't be specified if the resource is not in the %s namespace", vars.Namespace)
40+
}
41+
42+
return nil
43+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package webhook
2+
3+
import (
4+
"testing"
5+
6+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7+
8+
. "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
9+
"github.com/k8snetworkplumbingwg/sriov-network-operator/controllers"
10+
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
11+
)
12+
13+
func TestValidate_NetworkNamespace(t *testing.T) {
14+
defer func(previous string) { vars.Namespace = previous }(vars.Namespace)
15+
vars.Namespace = "operator-namespace"
16+
17+
testCases := []struct {
18+
name string
19+
network controllers.NetworkCRInstance
20+
shouldFail bool
21+
}{
22+
{
23+
name: "SriovNetwork in operator namespace with empty NetworkNamespace",
24+
network: &SriovNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "operator-namespace"}, Spec: SriovNetworkSpec{NetworkNamespace: ""}},
25+
shouldFail: false,
26+
},
27+
{
28+
name: "SriovNetwork in operator namespace with custom NetworkNamespace",
29+
network: &SriovNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "operator-namespace"}, Spec: SriovNetworkSpec{NetworkNamespace: "xxx"}},
30+
shouldFail: false,
31+
},
32+
{
33+
name: "SriovNetwork in custom namespace with empty NetworkNamespace",
34+
network: &SriovNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "xxx"}, Spec: SriovNetworkSpec{NetworkNamespace: ""}},
35+
shouldFail: false,
36+
},
37+
{
38+
name: "SriovIBNetwork in operator namespace with empty NetworkNamespace",
39+
network: &SriovIBNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "operator-namespace"}, Spec: SriovIBNetworkSpec{NetworkNamespace: ""}},
40+
shouldFail: false,
41+
},
42+
{
43+
name: "SriovIBNetwork in operator namespace with custom NetworkNamespace",
44+
network: &SriovIBNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "operator-namespace"}, Spec: SriovIBNetworkSpec{NetworkNamespace: "xxx"}},
45+
shouldFail: false,
46+
},
47+
{
48+
name: "SriovIBNetwork in custom namespace with empty NetworkNamespace",
49+
network: &SriovIBNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "xxx"}, Spec: SriovIBNetworkSpec{NetworkNamespace: ""}},
50+
shouldFail: false,
51+
},
52+
{
53+
name: "OVSNetwork in operator namespace with empty NetworkNamespace",
54+
network: &OVSNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "operator-namespace"}, Spec: OVSNetworkSpec{NetworkNamespace: ""}},
55+
shouldFail: false,
56+
},
57+
{
58+
name: "OVSNetwork in operator namespace with custom NetworkNamespace",
59+
network: &OVSNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "operator-namespace"}, Spec: OVSNetworkSpec{NetworkNamespace: "xxx"}},
60+
shouldFail: false,
61+
},
62+
{
63+
name: "OVSNetwork in custom namespace with empty NetworkNamespace",
64+
network: &OVSNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "xxx"}, Spec: OVSNetworkSpec{NetworkNamespace: ""}},
65+
shouldFail: false,
66+
},
67+
{
68+
name: "SriovNetwork in custom namespace with custom NetworkNamespace",
69+
network: &SriovNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "xxx"}, Spec: SriovNetworkSpec{NetworkNamespace: "yyy"}},
70+
shouldFail: true,
71+
},
72+
{
73+
name: "SriovIBNetwork in custom namespace with custom NetworkNamespace",
74+
network: &SriovIBNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "xxx"}, Spec: SriovIBNetworkSpec{NetworkNamespace: "yyy"}},
75+
shouldFail: true,
76+
},
77+
{
78+
name: "OVSNetwork in custom namespace with custom NetworkNamespace",
79+
network: &OVSNetwork{ObjectMeta: metav1.ObjectMeta{Namespace: "xxx"}, Spec: OVSNetworkSpec{NetworkNamespace: "yyy"}},
80+
shouldFail: true,
81+
},
82+
}
83+
84+
for _, tc := range testCases {
85+
t.Run(tc.name, func(t *testing.T) {
86+
err := validateNetworkNamespace(tc.network)
87+
if tc.shouldFail && err == nil {
88+
t.Error("expected error but got none")
89+
}
90+
if !tc.shouldFail && err != nil {
91+
t.Errorf("expected no error but got: %v", err)
92+
}
93+
})
94+
}
95+
}

pkg/webhook/webhook.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,49 @@ func ValidateCustomResource(ar v1.AdmissionReview) *v1.AdmissionResponse {
9797
Reason: metav1.StatusReason(err.Error()),
9898
}
9999
}
100+
101+
case "SriovNetwork":
102+
network := sriovnetworkv1.SriovNetwork{}
103+
104+
err = json.Unmarshal(raw, &network)
105+
if err != nil {
106+
log.Log.Error(err, "failed to unmarshal object")
107+
return toV1AdmissionResponse(err)
108+
}
109+
110+
if reviewResponse.Allowed, reviewResponse.Warnings, err = validateSriovNetwork(&network, ar.Request.Operation); err != nil {
111+
reviewResponse.Result = &metav1.Status{
112+
Reason: metav1.StatusReason(err.Error()),
113+
}
114+
}
115+
case "SriovIBNetwork":
116+
network := sriovnetworkv1.SriovIBNetwork{}
117+
118+
err = json.Unmarshal(raw, &network)
119+
if err != nil {
120+
log.Log.Error(err, "failed to unmarshal object")
121+
return toV1AdmissionResponse(err)
122+
}
123+
124+
if reviewResponse.Allowed, reviewResponse.Warnings, err = validateSriovIBNetwork(&network, ar.Request.Operation); err != nil {
125+
reviewResponse.Result = &metav1.Status{
126+
Reason: metav1.StatusReason(err.Error()),
127+
}
128+
}
129+
case "OVSNetwork":
130+
network := sriovnetworkv1.OVSNetwork{}
131+
132+
err = json.Unmarshal(raw, &network)
133+
if err != nil {
134+
log.Log.Error(err, "failed to unmarshal object")
135+
return toV1AdmissionResponse(err)
136+
}
137+
138+
if reviewResponse.Allowed, reviewResponse.Warnings, err = validateOVSNetwork(&network, ar.Request.Operation); err != nil {
139+
reviewResponse.Result = &metav1.Status{
140+
Reason: metav1.StatusReason(err.Error()),
141+
}
142+
}
100143
}
101144

102145
return &reviewResponse

0 commit comments

Comments
 (0)