Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maintained Apps: define app list, implement ingestion #21946

Merged
merged 12 commits into from
Sep 10, 2024

Conversation

mna
Copy link
Member

@mna mna commented Sep 10, 2024

Partial implementation of #21773 , a subsequent PR will take care of creating the cron job and some changes needed to the ingestion, should not impact work that needs to read from the library.

Checklist for submitter

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.
  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements)
  • Added/updated tests

Copy link
Member Author

@mna mna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the diff is huge, but a lot of this is testdata (the ~20 json files added for testing).

// StringOr is a JSON value that can be a string or a different type of object
// (e.g. somewhat common for a string or an array of strings, but can also be
// a string or an object, etc.).
type StringOr[T any] struct {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is not technically an "optional JSON" type, but I feel it fits better in this package than floating elsewhere or creating another JSON-related package. Maybe we should rename the package, but I didn't go that far. Happy to adjust if anyone has a strong preference!

}

func testIngestWithBrew(t *testing.T, ds *Datastore) {
if os.Getenv("NETWORK_TEST") == "" {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test calls the real brew API, so I put it behind that existing NETWORK_TEST env var. I believe this will still run on each CI, though. Are we ok doing this? It will generate a bit of load on the API, but on the other hand it's good to catch any unforeseen changes on their part.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

random thought: could we cache their responses in some other repo and then we only hit their API infrequently (once a day or something)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, I don't know ideally I'd like something easier, maybe we don't run that test in normal CI and only in our daily runs? I'll take a look if we already do this for some tests?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm looks like we don't really do that at the moment... it could run only if the race detector is enabled, but that feels hacky... I don't know, I'll leave it like that for now but open to ideas/suggestions! Maybe it's not that bad to let it run too, given that we will hit it daily from all fleet installations...

@@ -0,0 +1,97 @@
[
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the (embedded) JSON file that defines the apps we're interested in (the maintained apps). It contains bits of metadata information that we can't get otherwise from the Brew API.

case http.StatusOK:
// success, go on
case http.StatusNotFound:
// TODO: delete the existing entry? do nothing and succeed? doing the latter for now.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think doing nothing makes sense in that case? In all likelihood, the existing entry (if any) would still work - its installer URL is not tied to brew.


func installScriptForApp(app maintainedApp, cask *brewCask) string {
// TODO: implement install script based on cask and app installer format
return "install"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that logic part of the dmg/zip ticket?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this will be a significant effort. See

type brewArtifact struct {
App []string `json:"app"`
// Pkg is a bit like Binary, it is an array with a string and an object as
// first two elements. The object has a choices field with an array of
// objects. See Microsoft Edge.
Pkg []optjson.StringOr[*brewPkgChoices] `json:"pkg"`
Uninstall []*brewUninstall `json:"uninstall"`
Zap []*brewZap `json:"zap"`
// Binary is an array with a string and an object as first two elements. See
// the "docker" and "firefox" casks.
Binary []optjson.StringOr[*brewBinaryTarget] `json:"binary"`
}
type brewPkgChoices struct {
// At the moment we don't care about the "choices" field on the pkg.
Choices []any `json:"choices"`
}
type brewBinaryTarget struct {
Target string `json:"target"`
}
// unlike brewArtifact, a single brewUninstall can have many fields set.
// All fields can have one or multiple strings (string or []string).
type brewUninstall struct {
LaunchCtl optjson.StringOr[[]string] `json:"launchctl"`
Quit optjson.StringOr[[]string] `json:"quit"`
PkgUtil optjson.StringOr[[]string] `json:"pkgutil"`
Script optjson.StringOr[[]string] `json:"script"`
// format: [0]=signal, [1]=process name
Signal optjson.StringOr[[]string] `json:"signal"`
Delete optjson.StringOr[[]string] `json:"delete"`
RmDir optjson.StringOr[[]string] `json:"rmdir"`
}
// same as brewUninstall, can be []string or string (see Microsoft Teams).
type brewZap struct {
Trash optjson.StringOr[[]string] `json:"trash"`
RmDir optjson.StringOr[[]string] `json:"rmdir"`
}
for a look at all the options we need to take into account (in addition to the dmg/zip extraction).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those testdata files are a snapshot in time of what brew API returns for our apps. Useful for tests, to build a library from well-known, static values.

// It returns the expected results of the ingestion as a slice of
// fleet.MaintainedApps with only a few fields filled - the result of
// unmarshaling the testdata/expected_apps.json file.
func IngestMaintainedApps(t *testing.T, ds fleet.Datastore) []*fleet.MaintainedApp {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heads-up, those should generally be used for tests when the maintained apps are needed, will generate a cohesive, valid set of apps.

// ExpectedAppTokens returns the list of app tokens (unique identifier) that are
// expected to be in the maintained apps library after ingestion. The tokens are
// taken from the apps.json list.
func ExpectedAppTokens(t *testing.T) []string {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is mostly useful to lightly validate that the real brew API test returned the expected tokens.

@mna mna marked this pull request as ready for review September 10, 2024 15:36
@mna mna requested a review from a team as a code owner September 10, 2024 15:36
Copy link

codecov bot commented Sep 10, 2024

Codecov Report

Attention: Patch coverage is 86.39053% with 23 lines in your changes missing coverage. Please review.

Please upload report for BASE (feat-fleet-app-library@14b6546). Learn more about missing BASE report.

Files with missing lines Patch % Lines
server/mdm/maintainedapps/ingest.go 84.61% 6 Missing and 6 partials ⚠️
server/mdm/maintainedapps/testing_utils.go 79.41% 6 Missing and 1 partial ⚠️
server/datastore/mysql/maintained_apps.go 88.23% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@                    Coverage Diff                    @@
##             feat-fleet-app-library   #21946   +/-   ##
=========================================================
  Coverage                          ?   64.98%           
=========================================================
  Files                             ?     1497           
  Lines                             ?   116509           
  Branches                          ?     3450           
=========================================================
  Hits                              ?    75715           
  Misses                            ?    33769           
  Partials                          ?     7025           
Flag Coverage Δ
backend 66.22% <86.39%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@jahzielv jahzielv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! just 1 small question

},
{
"identifier": "box-drive",
"bundle_identifier": "???",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this ("???") the expected string to use if we don't know the bundle identifier?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:D No, good thing to mention indeed, this will need to be figured out, I just couldn't find that app in dogfood to grab the bundle identifier. I'll try to get those filled for the next PR, testing those apps on my mac mini.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added it as a checkbox to my ticket just to make sure I don't forget about it, thanks!

@mna mna merged commit 9abd5a5 into feat-fleet-app-library Sep 10, 2024
22 checks passed
@mna mna deleted the mna-21773-ingest-fleet-library branch September 10, 2024 17:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants