use only the UUID part of external_host_identifier for Puppet runs (#13176)

related to #12483, we have found out that in distributed scenarios, the
URL of the Puppet server used for the request is appended to the
identifier, and it can be different between `/preassign` and `/match`
calls.

to account for this, we're only grabbing the first 36 characters of the
identifier.
This commit is contained in:
Roberto Dip 2023-08-07 12:41:13 -03:00 committed by GitHub
parent 9ae3aa8036
commit 8fda48db8b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 4 deletions

View file

@ -0,0 +1 @@
* Fix delays applying profiles when the Puppet module is used in distributed scenarios.

View file

@ -18,6 +18,11 @@ const (
// must be reasonably longer than the expected time to make all
// PreassignProfile calls and the final matcher call.
preassignKeyExpiration = 1 * time.Hour
// in distributed scenarios, the external host identifier might come up
// with a suffix that varies from server to server. To account for that
// we only take into account the first 36 runes from the identifier.
// See https://github.com/fleetdm/fleet/issues/12483 for more info.
preassignKeySuffixMaxLen = 36
)
type profileMatcher struct {
@ -151,5 +156,17 @@ func (p *profileMatcher) RetrieveProfiles(ctx context.Context, externalHostIdent
}
func keyForExternalHostIdentifier(externalHostIdentifier string) string {
return preassignKeyPrefix + externalHostIdentifier
return preassignKeyPrefix + firstNRunes(externalHostIdentifier, preassignKeySuffixMaxLen)
}
// firstNRunes grabs the first N runes from the provided string
func firstNRunes(s string, n int) string {
i := 0
for j := range s {
if i == n {
return s[:j]
}
i++
}
return s
}

View file

@ -326,6 +326,23 @@ func TestPreassignProfileValidation(t *testing.T) {
}
}
func TestKeyForExternalHostIdentifier(t *testing.T) {
cases := []struct {
in string
want string
}{
{"", ""},
{"abcd", "abcd"},
{"6f36ab2c-1a40-429b-9c9d-07c9029f4aa8", "6f36ab2c-1a40-429b-9c9d-07c9029f4aa8"},
{"6f36ab2c-1a40-429b-9c9d-07c9029f4aa8-puppetcompiler06.test.example.com", "6f36ab2c-1a40-429b-9c9d-07c9029f4aa8"},
}
for _, c := range cases {
got := keyForExternalHostIdentifier(c.in)
require.Equal(t, preassignKeyPrefix+c.want, got)
}
}
func generateProfile(name, ident, typ, uuid string) []byte {
return []byte(fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

View file

@ -672,11 +672,15 @@ func (s *integrationMDMTestSuite) TestPuppetMatchPreassignProfiles() {
}}, http.StatusNoContent, "team_id", fmt.Sprint(tm4.ID))
// preassign the MDM host to prof1 and prof4, should match existing team tm2
s.Do("POST", "/api/latest/fleet/mdm/apple/profiles/preassign", preassignMDMAppleProfileRequest{MDMApplePreassignProfilePayload: fleet.MDMApplePreassignProfilePayload{ExternalHostIdentifier: "mdm1", HostUUID: mdmHost.UUID, Profile: prof1, Group: "g1"}}, http.StatusNoContent)
s.Do("POST", "/api/latest/fleet/mdm/apple/profiles/preassign", preassignMDMAppleProfileRequest{MDMApplePreassignProfilePayload: fleet.MDMApplePreassignProfilePayload{ExternalHostIdentifier: "mdm1", HostUUID: mdmHost.UUID, Profile: prof4, Group: "g4"}}, http.StatusNoContent)
//
// additionally, use external host identifiers with different
// suffixes to simulate real world distributed scenarios where more
// than one puppet server might be running at the time.
s.Do("POST", "/api/latest/fleet/mdm/apple/profiles/preassign", preassignMDMAppleProfileRequest{MDMApplePreassignProfilePayload: fleet.MDMApplePreassignProfilePayload{ExternalHostIdentifier: "6f36ab2c-1a40-429b-9c9d-07c9029f4aa8-puppetcompiler06.test.example.com", HostUUID: mdmHost.UUID, Profile: prof1, Group: "g1"}}, http.StatusNoContent)
s.Do("POST", "/api/latest/fleet/mdm/apple/profiles/preassign", preassignMDMAppleProfileRequest{MDMApplePreassignProfilePayload: fleet.MDMApplePreassignProfilePayload{ExternalHostIdentifier: "6f36ab2c-1a40-429b-9c9d-07c9029f4aa8-puppetcompiler01.test.example.com", HostUUID: mdmHost.UUID, Profile: prof4, Group: "g4"}}, http.StatusNoContent)
// match with the mdm host succeeds and assigns it to tm2
s.Do("POST", "/api/latest/fleet/mdm/apple/profiles/match", matchMDMApplePreassignmentRequest{ExternalHostIdentifier: "mdm1"}, http.StatusNoContent)
s.Do("POST", "/api/latest/fleet/mdm/apple/profiles/match", matchMDMApplePreassignmentRequest{ExternalHostIdentifier: "6f36ab2c-1a40-429b-9c9d-07c9029f4aa8-puppetcompiler03.test.example.com"}, http.StatusNoContent)
// the host is now part of that team
h, err = s.ds.Host(ctx, mdmHost.ID)