Skip to content

Commit 52ddcea

Browse files
kosigz-lyftkosigz
andauthored
Validate that Flyte labels are K8s compliant (flyteorg#131)
* v0.0.1 * fix build * wip * wip * no build brb * no build brb * rm k8s validation * wip * feedback * lowercase error strings is convention in golang * also add validation on project creation * test * add integration test? * last line * rename some stuff * lint * done Co-authored-by: Konstantin Gizdarski <[email protected]>
1 parent f305d39 commit 52ddcea

File tree

4 files changed

+104
-0
lines changed

4 files changed

+104
-0
lines changed

pkg/manager/impl/project_manager.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ func (m *ProjectManager) UpdateProject(ctx context.Context, projectUpdate admin.
7070
return nil, err
7171
}
7272

73+
// Run validation on the request, specifically checking for labels, and return err if validation does not succeed.
74+
err = validation.ValidateProjectLabels(projectUpdate)
75+
if err != nil {
76+
return nil, err
77+
}
78+
7379
// Transform the provided project into a model and apply to the DB.
7480
projectUpdateModel := transformers.CreateProjectModel(&projectUpdate)
7581
err = projectRepo.UpdateProject(ctx, projectUpdateModel)

pkg/manager/impl/project_manager_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,37 @@ func TestProjectManager_CreateProjectError(t *testing.T) {
124124
})
125125
assert.EqualError(t, err, "Domains are currently only set system wide. Please retry without domains included in your request.")
126126
}
127+
128+
func TestProjectManager_CreateProjectErrorDueToBadLabels(t *testing.T) {
129+
mockRepository := repositoryMocks.NewMockRepository()
130+
mockRepository.ProjectRepo().(*repositoryMocks.MockProjectRepo).CreateFunction = func(
131+
ctx context.Context, namespace models.Project) error {
132+
return errors.New("uh oh")
133+
}
134+
projectManager := NewProjectManager(mockRepository,
135+
runtimeMocks.NewMockConfigurationProvider(
136+
getMockApplicationConfigForProjectManagerTest(), nil, nil, nil, nil, nil))
137+
_, err := projectManager.CreateProject(context.Background(), admin.ProjectRegisterRequest{
138+
Project: &admin.Project{
139+
Id: "flyte-project-id",
140+
Name: "flyte-project-name",
141+
Description: "flyte-project-description",
142+
},
143+
})
144+
assert.EqualError(t, err, "uh oh")
145+
146+
_, err = projectManager.CreateProject(context.Background(), admin.ProjectRegisterRequest{
147+
Project: &admin.Project{
148+
Id: "flyte-project-id",
149+
Name: "flyte-project-name",
150+
Description: "flyte-project-description",
151+
Labels: &admin.Labels{
152+
Values: map[string]string{
153+
"foo": "#badlabel",
154+
"bar": "baz",
155+
},
156+
},
157+
},
158+
})
159+
assert.EqualError(t, err, "invalid label value [#badlabel]: [a DNS-1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')]")
160+
}

pkg/manager/impl/validation/project_validator.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ func ValidateProjectRegisterRequest(request admin.ProjectRegisterRequest) error
2424
if err := ValidateEmptyStringField(request.Project.Id, projectID); err != nil {
2525
return err
2626
}
27+
if err := ValidateProjectLabels(*request.Project); err != nil {
28+
return err
29+
}
2730
if errs := validation.IsDNS1123Label(request.Project.Id); len(errs) > 0 {
2831
return errors.NewFlyteAdminErrorf(codes.InvalidArgument, "invalid project id [%s]: %v", request.Project.Id, errs)
2932
}
@@ -40,6 +43,13 @@ func ValidateProjectRegisterRequest(request admin.ProjectRegisterRequest) error
4043
return nil
4144
}
4245

46+
func ValidateProjectLabels(request admin.Project) error {
47+
if err := ValidateProjectLabelsAlphanumeric(request); err != nil {
48+
return err
49+
}
50+
return nil
51+
}
52+
4353
// Validates that a specified project and domain combination has been registered and exists in the db.
4454
func ValidateProjectAndDomain(
4555
ctx context.Context, db repositories.RepositoryInterface, config runtimeInterfaces.ApplicationConfiguration, projectID, domainID string) error {
@@ -62,3 +72,20 @@ func ValidateProjectAndDomain(
6272
}
6373
return nil
6474
}
75+
76+
// Given an admin.Project, checks if the project has labels and if it does, checks if the labels are K8s compliant,
77+
// i.e. alphanumeric + - and _
78+
func ValidateProjectLabelsAlphanumeric(request admin.Project) error {
79+
if request.Labels == nil || len(request.Labels.Values) == 0 {
80+
return nil
81+
}
82+
for key, value := range request.Labels.Values {
83+
if errs := validation.IsDNS1123Label(key); len(errs) > 0 {
84+
return errors.NewFlyteAdminErrorf(codes.InvalidArgument, "invalid label key [%s]: %v", key, errs)
85+
}
86+
if errs := validation.IsDNS1123Label(value); len(errs) > 0 {
87+
return errors.NewFlyteAdminErrorf(codes.InvalidArgument, "invalid label value [%s]: %v", value, errs)
88+
}
89+
}
90+
return nil
91+
}

tests/project.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,40 @@ func TestUpdateProjectLabels(t *testing.T) {
160160
assert.Equal(t, barExists, true)
161161
assert.Equal(t, barVal, "baz")
162162
}
163+
164+
func TestUpdateProjectLabels_BadLabels(t *testing.T) {
165+
ctx := context.Background()
166+
client, conn := GetTestAdminServiceClient()
167+
defer conn.Close()
168+
169+
// Create a new project.
170+
req := admin.ProjectRegisterRequest{
171+
Project: &admin.Project{
172+
Id: "potato",
173+
Name: "spud",
174+
},
175+
}
176+
_, err := client.RegisterProject(ctx, &req)
177+
assert.Nil(t, err)
178+
179+
// Verify the project has been registered.
180+
projects, err := client.ListProjects(ctx, &admin.ProjectListRequest{})
181+
assert.Nil(t, err)
182+
assert.NotEmpty(t, projects.Projects)
183+
184+
// Attempt to modify the name of the Project. Labels and name should be
185+
// modified.
186+
_, err = client.UpdateProject(ctx, &admin.Project{
187+
Id: "potato",
188+
Name: "foobar",
189+
Labels: &admin.Labels{
190+
Values: map[string]string{
191+
"foo": "#bar",
192+
"bar": "baz",
193+
},
194+
},
195+
})
196+
197+
// Assert that update went through without an error.
198+
assert.EqualError(t, err, "invalid label value [#bar]: [a DNS-1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')]")
199+
}

0 commit comments

Comments
 (0)