Add clearer error for VPP token constraint failure (#21967)

#21890
This commit is contained in:
Dante Catalfamo 2024-09-11 14:11:28 -04:00 committed by GitHub
parent ca9874ea79
commit 8ad0d59016
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 43 additions and 2 deletions

View file

@ -0,0 +1 @@
- Improve messaging for VPP token constraint errors

View file

@ -16,6 +16,7 @@ import (
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm"
"github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
@ -780,6 +781,7 @@ TEAMLOOP:
}
func (ds *Datastore) UpdateVPPTokenTeams(ctx context.Context, id uint, teams []uint) (*fleet.VPPTokenDB, error) {
stmtTeamName := `SELECT name FROM teams WHERE id = ?`
stmtRemove := `DELETE FROM vpp_token_teams WHERE vpp_token_id = ?`
stmtInsert := `
INSERT INTO
@ -858,6 +860,17 @@ func (ds *Datastore) UpdateVPPTokenTeams(ctx context.Context, id uint, teams []u
})
if err != nil {
var mysqlErr *mysql.MySQLError
// https://dev.mysql.com/doc/mysql-errors/8.4/en/server-error-reference.html#error_er_dup_entry
if errors.As(err, &mysqlErr) && IsDuplicate(err) {
var dupeTeamID uint
var dupeTeamName string
fmt.Sscanf(mysqlErr.Message, "Duplicate entry '%d' for", &dupeTeamID)
if err := sqlx.GetContext(ctx, ds.reader(ctx), &dupeTeamName, stmtTeamName, dupeTeamID); err != nil {
return nil, ctxerr.Wrap(ctx, err, "getting team name for vpp token conflict error")
}
return nil, ctxerr.Wrap(ctx, fleet.ErrVPPTokenTeamConstraint{Name: dupeTeamName, ID: &dupeTeamID})
}
return nil, ctxerr.Wrap(ctx, err, "modifying vpp token team associations")
}
@ -1127,7 +1140,7 @@ func checkVPPNullTeam(ctx context.Context, tx sqlx.ExtContext, currentID *uint,
}
if allTeamsFound && currentID != nil && *currentID != id {
return ctxerr.Wrap(ctx, errors.New("All teams token already exists"))
return ctxerr.Wrap(ctx, fleet.ErrVPPTokenTeamConstraint{Name: fleet.ReservedNameAllTeams})
}
if nullTeam != fleet.NullTeamNone {
@ -1139,7 +1152,7 @@ func checkVPPNullTeam(ctx context.Context, tx sqlx.ExtContext, currentID *uint,
return ctxerr.Wrap(ctx, err, "scanning row in check vpp token null team")
}
if currentID == nil || *currentID != id {
return ctxerr.Errorf(ctx, "vpp token for team %s already exists", nullTeam)
return ctxerr.Wrap(ctx, fleet.ErrVPPTokenTeamConstraint{Name: nullTeam.PrettyName()})
}
}

View file

@ -981,8 +981,10 @@ func testVPPTokensCRUD(t *testing.T, ds *Datastore) {
assert.Error(t, err)
_, err = ds.UpdateVPPTokenTeams(ctx, tokBadConstraint.ID, []uint{team.ID})
assert.Error(t, err)
assert.ErrorContains(t, err, "\"Kritters\" team already has a VPP token.")
_, err = ds.UpdateVPPTokenTeams(ctx, tokBadConstraint.ID, []uint{0})
assert.Error(t, err)
assert.ErrorContains(t, err, "\"No team\" team already has a VPP token.")
toks, err = ds.ListVPPTokens(ctx)
assert.NoError(t, err)

View file

@ -801,6 +801,17 @@ const (
NullTeamNoTeam NullTeamType = "noteam"
)
func (n NullTeamType) PrettyName() string {
switch n {
case NullTeamAllTeams:
return ReservedNameAllTeams
case NullTeamNoTeam:
return ReservedNameNoTeam
default:
return string(n)
}
}
type AppleDevice int
const (

View file

@ -1,6 +1,7 @@
package fleet
import (
"fmt"
"time"
)
@ -67,3 +68,12 @@ type VPPAppStatusSummary struct {
// Failed is the number of hosts that have the VPP app installation failed.
Failed uint `json:"failed" db:"failed"`
}
type ErrVPPTokenTeamConstraint struct {
Name string
ID *uint
}
func (e ErrVPPTokenTeamConstraint) Error() string {
return fmt.Sprintf("Error: %q team already has a VPP token. Each team can only have one VPP token.", e.Name)
}

View file

@ -557,6 +557,10 @@ func (svc *Service) ModifyAppConfig(ctx context.Context, p []byte, applyOpts fle
if appConfig.MDM.VolumePurchasingProgram.Set && appConfig.MDM.VolumePurchasingProgram.Valid {
for tokenID, tokenTeams := range vppAssignments {
if _, err := svc.ds.UpdateVPPTokenTeams(ctx, tokenID, tokenTeams); err != nil {
var errTokConstraint fleet.ErrVPPTokenTeamConstraint
if errors.As(err, &errTokConstraint) {
return nil, ctxerr.Wrap(ctx, fleet.NewUserMessageError(errTokConstraint, http.StatusConflict))
}
return nil, ctxerr.Wrap(ctx, err, "saving ABM token assignments")
}
}