Skip to content

Commit

Permalink
Store labels for profiles in the database (#2770)
Browse files Browse the repository at this point in the history
* Add a labels column to profiles, defaulting to an empty set

* Disallow creating profiles with labels through non-subscription API and updating labels

* Enhance CreateProfile and UpdateProfile with labels
  • Loading branch information
jhrozek authored Mar 25, 2024
1 parent eb025e1 commit d7b9e80
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 13 deletions.
17 changes: 17 additions & 0 deletions database/migrations/000036_profile_label.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- Copyright 2024 Stacklok, Inc
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

ALTER TABLE profiles DROP COLUMN labels;

DROP INDEX IF EXISTS idx_entity_profiles_profile_id;
20 changes: 20 additions & 0 deletions database/migrations/000036_profile_label.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- Copyright 2024 Stacklok, Inc
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.

ALTER TABLE profiles ADD COLUMN labels TEXT[] DEFAULT '{}';

-- This index is a bit besides the point of profile labels, but while profiling the searches
-- we noticed that the profile_id index was missing and it was causing a full table scan
-- which was more costly then the subsequent label filtering on the subset of profiles.
CREATE INDEX idx_entity_profiles_profile_id ON entity_profiles(profile_id);
8 changes: 5 additions & 3 deletions database/query/profiles.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ INSERT INTO profiles (
name,
provider_id,
subscription_id,
display_name
) VALUES ($1, $2, $3, $4, $5, sqlc.arg(provider_id), sqlc.narg(subscription_id), sqlc.arg(display_name)) RETURNING *;
display_name,
labels
) VALUES ($1, $2, $3, $4, $5, sqlc.arg(provider_id), sqlc.narg(subscription_id), sqlc.arg(display_name), sqlc.arg(labels)::text[]) RETURNING *;

-- name: UpdateProfile :one
UPDATE profiles SET
remediate = $3,
alert = $4,
updated_at = NOW(),
display_name = sqlc.arg(display_name)
display_name = sqlc.arg(display_name),
labels = sqlc.arg(labels)::TEXT[]
WHERE id = $1 AND project_id = $2 RETURNING *;

-- name: CreateProfileForEntity :one
Expand Down
1 change: 1 addition & 0 deletions internal/controlplane/handlers_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ func (s *Server) PatchProfile(ctx context.Context, ppr *minderv1.PatchProfileReq
params := db.UpdateProfileParams{
ID: profileID,
ProjectID: entityCtx.Project.ID,
Labels: oldProfile.Labels, // since labels are meant to be used by subscription profiles, just copy them over
}

// we check the pointers explicitly because the zero value of a string is valid
Expand Down
1 change: 1 addition & 0 deletions internal/db/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 28 additions & 10 deletions internal/db/profiles.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions internal/marketplaces/namespaces/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package namespaces

import (
"errors"
"slices"
"strings"

"github.com/google/uuid"
Expand Down Expand Up @@ -68,3 +69,21 @@ func DoesSubscriptionIDMatch(subscriptionID uuid.UUID, dbSubscriptionID uuid.Nul
}
return nil
}

// ValidateLabelsPresence makes sure that only profiles that belong to a subscription
// bundle have labels
func ValidateLabelsPresence(labels []string, subscriptionID uuid.UUID) error {
if subscriptionID == uuid.Nil && len(labels) > 0 {
return errors.New("labels can only be applied to profiles from a subscription bundle")
}
return nil
}

// ValidateLabelsUpdate ensures that labels cannot be updated
func ValidateLabelsUpdate(labels, dbLabels []string) error {
isEqual := slices.Equal(labels, dbLabels)
if !isEqual {
return errors.New("labels cannot be updated")
}
return nil
}
10 changes: 10 additions & 0 deletions internal/profiles/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ func (p *profileService) CreateSubscriptionProfile(
return nil, status.Errorf(codes.InvalidArgument, "name failed namespace validation: %v", err)
}

if err = namespaces.ValidateLabelsPresence(profile.GetLabels(), subscriptionID); err != nil {
return nil, status.Errorf(codes.InvalidArgument, "labels failed namespace validation: %v", err)
}

// Adds default rule names, if not present
PopulateRuleNames(profile)

Expand All @@ -161,6 +165,7 @@ func (p *profileService) CreateSubscriptionProfile(
ProjectID: projectID,
Name: profile.GetName(),
DisplayName: displayName,
Labels: profile.GetLabels(),
Remediate: db.ValidateRemediateType(profile.GetRemediate()),
Alert: db.ValidateAlertType(profile.GetAlert()),
SubscriptionID: uuid.NullUUID{UUID: subscriptionID, Valid: subscriptionID != uuid.Nil},
Expand Down Expand Up @@ -295,6 +300,7 @@ func (p *profileService) UpdateSubscriptionProfile(
ProjectID: projectID,
ID: oldDBProfile.ID,
DisplayName: displayName,
Labels: profile.GetLabels(),
Remediate: db.ValidateRemediateType(profile.GetRemediate()),
Alert: db.ValidateAlertType(profile.GetAlert()),
})
Expand Down Expand Up @@ -484,6 +490,10 @@ func validateProfileUpdate(
return util.UserVisibleError(codes.InvalidArgument, "cannot change profile provider")
}

if err := namespaces.ValidateLabelsUpdate(new.GetLabels(), old.Labels); err != nil {
return util.UserVisibleError(codes.InvalidArgument, "labels update failed validation: %v", err)
}

return nil
}

Expand Down

0 comments on commit d7b9e80

Please sign in to comment.