Skip to content

Commit 1eb8e54

Browse files
fix: validate the new project state for consistency (#1118)
chore: additional unit tests
1 parent 616e4dd commit 1eb8e54

File tree

2 files changed

+158
-6
lines changed

2 files changed

+158
-6
lines changed

pkg/resources/management.cattle.io/v3/project/validator.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func (a *admitter) admitCommonCreateUpdate(oldProject, newProject *v3.Project) (
165165
if fieldErr != nil {
166166
return admission.ResponseBadRequest(fieldErr.Error()), nil
167167
}
168-
fieldErr, err = a.checkQuotaValues(&nsQuota.Limit, &projectQuota.Limit, oldProject)
168+
fieldErr, err = a.checkQuotaValues(&nsQuota.Limit, &projectQuota.Limit, newProject)
169169
if err != nil {
170170
return nil, fmt.Errorf("error checking quota values: %w", err)
171171
}
@@ -258,20 +258,24 @@ func checkQuotaFields(projectQuota *v3.ProjectResourceQuota, nsQuota *v3.Namespa
258258
return nil, nil
259259
}
260260

261-
func (a *admitter) checkQuotaValues(nsQuota, projectQuota *v3.ResourceQuotaLimit, oldProject *v3.Project) (*field.Error, error) {
261+
// checkQuotaValues ensures that the new quotas are consistent with the new
262+
// used-limit. checking against the old used-limit is contra-indicated as it
263+
// may contain bogus data, causing failure to validate. making it impossible to
264+
// replace a bogus used-limit with good values.
265+
func (a *admitter) checkQuotaValues(nsQuota, projectQuota *v3.ResourceQuotaLimit, newProject *v3.Project) (*field.Error, error) {
262266
// check quota on new project
263267
fieldErr, err := namespaceQuotaFits(nsQuota, projectQuota)
264268
if err != nil || fieldErr != nil {
265269
return fieldErr, err
266270
}
267271

268-
// if there is no old project or no quota on the old project, no further validation needed
269-
if oldProject == nil || oldProject.Spec.ResourceQuota == nil {
272+
// if there is no new project or no quota on the new project, no further validation needed
273+
if newProject == nil || newProject.Spec.ResourceQuota == nil {
270274
return nil, nil
271275
}
272276

273277
// check quota relative to used quota
274-
return usedQuotaFits(&oldProject.Spec.ResourceQuota.UsedLimit, projectQuota)
278+
return usedQuotaFits(&newProject.Spec.ResourceQuota.UsedLimit, projectQuota)
275279
}
276280

277281
func namespaceQuotaFits(namespaceQuota, projectQuota *v3.ResourceQuotaLimit) (*field.Error, error) {

pkg/resources/management.cattle.io/v3/project/validator_test.go

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ func TestProjectValidation(t *testing.T) {
990990
wantAllowed: false,
991991
},
992992
{
993-
name: "update with project quota less than used quota",
993+
name: "update with project quota less than used quota, quota changed",
994994
operation: admissionv1.Update,
995995
oldProject: &v3.Project{
996996
ObjectMeta: metav1.ObjectMeta{
@@ -1038,6 +1038,154 @@ func TestProjectValidation(t *testing.T) {
10381038
},
10391039
wantAllowed: false,
10401040
},
1041+
{
1042+
name: "update with project quota less than used quota, used quota changed",
1043+
operation: admissionv1.Update,
1044+
oldProject: &v3.Project{
1045+
ObjectMeta: metav1.ObjectMeta{
1046+
Name: "test",
1047+
Namespace: "testcluster",
1048+
},
1049+
Spec: v3.ProjectSpec{
1050+
ClusterName: "testcluster",
1051+
ResourceQuota: &v3.ProjectResourceQuota{
1052+
Limit: v3.ResourceQuotaLimit{
1053+
ConfigMaps: "100",
1054+
},
1055+
UsedLimit: v3.ResourceQuotaLimit{
1056+
ConfigMaps: "100",
1057+
},
1058+
},
1059+
NamespaceDefaultResourceQuota: &v3.NamespaceResourceQuota{
1060+
Limit: v3.ResourceQuotaLimit{
1061+
ConfigMaps: "50",
1062+
},
1063+
},
1064+
},
1065+
},
1066+
newProject: &v3.Project{
1067+
ObjectMeta: metav1.ObjectMeta{
1068+
Name: "test",
1069+
Namespace: "testcluster",
1070+
},
1071+
Spec: v3.ProjectSpec{
1072+
ClusterName: "testcluster",
1073+
ResourceQuota: &v3.ProjectResourceQuota{
1074+
Limit: v3.ResourceQuotaLimit{
1075+
ConfigMaps: "100",
1076+
},
1077+
UsedLimit: v3.ResourceQuotaLimit{
1078+
ConfigMaps: "1000",
1079+
},
1080+
},
1081+
NamespaceDefaultResourceQuota: &v3.NamespaceResourceQuota{
1082+
Limit: v3.ResourceQuotaLimit{
1083+
ConfigMaps: "50",
1084+
},
1085+
},
1086+
},
1087+
},
1088+
wantAllowed: false,
1089+
},
1090+
{
1091+
name: "update project with bogus used quota, to good used quota",
1092+
operation: admissionv1.Update,
1093+
oldProject: &v3.Project{
1094+
ObjectMeta: metav1.ObjectMeta{
1095+
Name: "test",
1096+
Namespace: "testcluster",
1097+
},
1098+
Spec: v3.ProjectSpec{
1099+
ClusterName: "testcluster",
1100+
ResourceQuota: &v3.ProjectResourceQuota{
1101+
Limit: v3.ResourceQuotaLimit{
1102+
ConfigMaps: "100",
1103+
},
1104+
UsedLimit: v3.ResourceQuotaLimit{
1105+
ConfigMaps: "xxxxxxxxxx",
1106+
},
1107+
},
1108+
NamespaceDefaultResourceQuota: &v3.NamespaceResourceQuota{
1109+
Limit: v3.ResourceQuotaLimit{
1110+
ConfigMaps: "50",
1111+
},
1112+
},
1113+
},
1114+
},
1115+
newProject: &v3.Project{
1116+
ObjectMeta: metav1.ObjectMeta{
1117+
Name: "test",
1118+
Namespace: "testcluster",
1119+
},
1120+
Spec: v3.ProjectSpec{
1121+
ClusterName: "testcluster",
1122+
ResourceQuota: &v3.ProjectResourceQuota{
1123+
Limit: v3.ResourceQuotaLimit{
1124+
ConfigMaps: "100",
1125+
},
1126+
UsedLimit: v3.ResourceQuotaLimit{
1127+
ConfigMaps: "100",
1128+
},
1129+
},
1130+
NamespaceDefaultResourceQuota: &v3.NamespaceResourceQuota{
1131+
Limit: v3.ResourceQuotaLimit{
1132+
ConfigMaps: "50",
1133+
},
1134+
},
1135+
},
1136+
},
1137+
wantAllowed: true,
1138+
},
1139+
{
1140+
name: "update project with good used quota, to bogus used quota",
1141+
operation: admissionv1.Update,
1142+
oldProject: &v3.Project{
1143+
ObjectMeta: metav1.ObjectMeta{
1144+
Name: "test",
1145+
Namespace: "testcluster",
1146+
},
1147+
Spec: v3.ProjectSpec{
1148+
ClusterName: "testcluster",
1149+
ResourceQuota: &v3.ProjectResourceQuota{
1150+
Limit: v3.ResourceQuotaLimit{
1151+
ConfigMaps: "100",
1152+
},
1153+
UsedLimit: v3.ResourceQuotaLimit{
1154+
ConfigMaps: "100",
1155+
},
1156+
},
1157+
NamespaceDefaultResourceQuota: &v3.NamespaceResourceQuota{
1158+
Limit: v3.ResourceQuotaLimit{
1159+
ConfigMaps: "50",
1160+
},
1161+
},
1162+
},
1163+
},
1164+
newProject: &v3.Project{
1165+
ObjectMeta: metav1.ObjectMeta{
1166+
Name: "test",
1167+
Namespace: "testcluster",
1168+
},
1169+
Spec: v3.ProjectSpec{
1170+
ClusterName: "testcluster",
1171+
ResourceQuota: &v3.ProjectResourceQuota{
1172+
Limit: v3.ResourceQuotaLimit{
1173+
ConfigMaps: "100",
1174+
},
1175+
UsedLimit: v3.ResourceQuotaLimit{
1176+
ConfigMaps: "xxxxxxxxxx",
1177+
},
1178+
},
1179+
NamespaceDefaultResourceQuota: &v3.NamespaceResourceQuota{
1180+
Limit: v3.ResourceQuotaLimit{
1181+
ConfigMaps: "50",
1182+
},
1183+
},
1184+
},
1185+
},
1186+
wantErr: true,
1187+
wantAllowed: false,
1188+
},
10411189
{
10421190
name: "update with fields changed in project quota less than used quota",
10431191
operation: admissionv1.Update,

0 commit comments

Comments
 (0)