Software SS: Update APIs with self_service (#19187)

This commit is contained in:
Dante Catalfamo 2024-05-23 09:47:04 -04:00 committed by GitHub
parent b55e2980c5
commit 87c4deb307
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 43 additions and 11 deletions

View file

@ -72,6 +72,7 @@ func (svc *Service) UploadSoftwareInstaller(ctx context.Context, payload *fleet.
SoftwarePackage: payload.Filename,
TeamName: teamName,
TeamID: payload.TeamID,
SelfService: payload.SelfService,
}); err != nil {
return ctxerr.Wrap(ctx, err, "creating activity for added software")
}
@ -116,6 +117,7 @@ func (svc *Service) DeleteSoftwareInstaller(ctx context.Context, titleID uint, t
SoftwarePackage: meta.Name,
TeamName: teamName,
TeamID: meta.TeamID,
SelfService: meta.SelfService,
}); err != nil {
return ctxerr.Wrap(ctx, err, "creating activity for deleted software")
}
@ -453,6 +455,7 @@ func (svc *Service) BatchSetSoftwareInstallers(ctx context.Context, tmName strin
PreInstallQuery: p.PreInstallQuery,
PostInstallScript: p.PostInstallScript,
InstallerFile: bytes.NewReader(bodyBytes),
SelfService: p.SelfService,
}
// set the filename before adding metadata, as it is used as fallback

View file

@ -104,8 +104,9 @@ INSERT INTO software_installers (
install_script_content_id,
pre_install_query,
post_install_script_content_id,
platform
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
platform,
self_service
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
args := []interface{}{
payload.TeamID,
@ -118,6 +119,7 @@ INSERT INTO software_installers (
payload.PreInstallQuery,
postInstallScriptID,
payload.Platform,
payload.SelfService,
}
res, err := ds.writer(ctx).ExecContext(ctx, stmt, args...)
@ -204,6 +206,7 @@ SELECT
si.pre_install_query,
si.post_install_script_content_id,
si.uploaded_at,
si.self_service,
COALESCE(st.name, '') AS software_title
%s
FROM
@ -299,7 +302,8 @@ SELECT
h.team_id AS host_team_id,
hsi.user_id AS user_id,
hsi.post_install_script_exit_code,
hsi.install_script_exit_code
hsi.install_script_exit_code,
hsi.self_service
FROM
host_software_installs hsi
JOIN hosts h ON h.id = hsi.host_id

View file

@ -1455,6 +1455,7 @@ type ActivityTypeAddedSoftware struct {
SoftwarePackage string `json:"software_package"`
TeamName *string `json:"team_name"`
TeamID *uint `json:"team_id"`
SelfService bool `json:"self_service"`
}
func (a ActivityTypeAddedSoftware) ActivityName() string {
@ -1477,6 +1478,7 @@ func (a ActivityTypeAddedSoftware) Documentation() (string, string, string) {
}
type ActivityTypeDeletedSoftware struct {
SelfService bool `json:"self_service"`
SoftwareTitle string `json:"software_title"`
SoftwarePackage string `json:"software_package"`
TeamName *string `json:"team_name"`

View file

@ -363,6 +363,7 @@ type SoftwareInstallerPayload struct {
PreInstallQuery string `json:"pre_install_query"`
InstallScript string `json:"install_script"`
PostInstallScript string `json:"post_install_script"`
SelfService bool `json:"self_service"`
}
type HostLockWipeStatus struct {

View file

@ -95,6 +95,9 @@ type SoftwareInstaller struct {
Status *SoftwareInstallerStatusSummary `json:"status,omitempty" db:"-"`
// SoftwareTitle is the title of the software pointed installed by this installer.
SoftwareTitle string `json:"-" db:"software_title"`
// SelfService indicates that the software can be installed by the
// end user without admin intervention
SelfService bool `json:"-" db:"self_service"`
}
// AuthzType implements authz.AuthzTyper.
@ -175,6 +178,9 @@ type HostSoftwareInstallerResult struct {
InstallScriptExitCode *int `json:"-" db:"install_script_exit_code"`
// PostInstallScriptExitCode is used internally to determine the output displayed to the user.
PostInstallScriptExitCode *int `json:"-" db:"post_install_script_exit_code"`
// SelfService indicates that the installation was queued by the
// end user and not an administrator
SelfService bool `json:"self_service" db:"self_service"`
}
const (
@ -252,6 +258,7 @@ type UploadSoftwareInstallerPayload struct {
Version string
Source string
Platform string
SelfService bool
}
// DownloadSoftwareInstallerPayload is the payload for downloading a software installer.

View file

@ -8970,7 +8970,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerUploadDownloadAndD
s.uploadSoftwareInstaller(payload, http.StatusOK, "")
// check activity
s.lastActivityOfTypeMatches(fleet.ActivityTypeAddedSoftware{}.ActivityName(), `{"software_title": "ruby", "software_package": "ruby.deb", "team_name": null, "team_id": null}`, 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeAddedSoftware{}.ActivityName(), `{"software_title": "ruby", "software_package": "ruby.deb", "team_name": null, "team_id": null, "self_service": false}`, 0)
// check the software installer
_, titleID := checkSoftwareInstaller(t, payload)
@ -9005,11 +9005,12 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerUploadDownloadAndD
PostInstallScript: "another post install script",
Filename: "ruby.deb",
// additional fields below are pre-populated so we can re-use the payload later for the test assertions
Title: "ruby",
Version: "1:2.5.1",
Source: "deb_packages",
StorageID: "df06d9ce9e2090d9cb2e8cd1f4d7754a803dc452bf93e3204e3acd3b95508628",
Platform: "linux",
Title: "ruby",
Version: "1:2.5.1",
Source: "deb_packages",
StorageID: "df06d9ce9e2090d9cb2e8cd1f4d7754a803dc452bf93e3204e3acd3b95508628",
Platform: "linux",
SelfService: true,
}
s.uploadSoftwareInstaller(payload, http.StatusOK, "")
@ -9017,7 +9018,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerUploadDownloadAndD
installerID, titleID := checkSoftwareInstaller(t, payload)
// check activity
s.lastActivityOfTypeMatches(fleet.ActivityTypeAddedSoftware{}.ActivityName(), fmt.Sprintf(`{"software_title": "ruby", "software_package": "ruby.deb", "team_name": "%s", "team_id": %d}`, createTeamResp.Team.Name, createTeamResp.Team.ID), 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeAddedSoftware{}.ActivityName(), fmt.Sprintf(`{"software_title": "ruby", "software_package": "ruby.deb", "team_name": "%s", "team_id": %d, "self_service": true}`, createTeamResp.Team.Name, createTeamResp.Team.ID), 0)
// upload again fails
s.uploadSoftwareInstaller(payload, http.StatusConflict, "already exists")
@ -9057,7 +9058,7 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerUploadDownloadAndD
s.Do("DELETE", fmt.Sprintf("/api/latest/fleet/software/%d/package", titleID), nil, http.StatusNoContent, "team_id", fmt.Sprintf("%d", *payload.TeamID))
// check activity
s.lastActivityOfTypeMatches(fleet.ActivityTypeDeletedSoftware{}.ActivityName(), fmt.Sprintf(`{"software_title": "ruby", "software_package": "ruby.deb", "team_name": "%s", "team_id": %d}`, createTeamResp.Team.Name, createTeamResp.Team.ID), 0)
s.lastActivityOfTypeMatches(fleet.ActivityTypeDeletedSoftware{}.ActivityName(), fmt.Sprintf(`{"software_title": "ruby", "software_package": "ruby.deb", "team_name": "%s", "team_id": %d, "self_service": true}`, createTeamResp.Team.Name, createTeamResp.Team.ID), 0)
})
}
@ -9714,6 +9715,9 @@ func (s *integrationEnterpriseTestSuite) uploadSoftwareInstaller(payload *fleet.
require.NoError(t, w.WriteField("install_script", payload.InstallScript))
require.NoError(t, w.WriteField("pre_install_query", payload.PreInstallQuery))
require.NoError(t, w.WriteField("post_install_script", payload.PostInstallScript))
if payload.SelfService {
require.NoError(t, w.WriteField("self_service", "true"))
}
w.Close()

View file

@ -20,6 +20,7 @@ type uploadSoftwareInstallerRequest struct {
InstallScript string
PreInstallQuery string
PostInstallScript string
SelfService bool
}
type uploadSoftwareInstallerResponse struct {
@ -79,6 +80,15 @@ func (uploadSoftwareInstallerRequest) DecodeRequest(ctx context.Context, r *http
decoded.PostInstallScript = val[0]
}
val, ok = r.MultipartForm.Value["self_service"]
if ok && len(val) > 0 && val[0] != "" {
parsed, err := strconv.ParseBool(val[0])
if err != nil {
return nil, &fleet.BadRequestError{Message: fmt.Sprintf("failed to decode self_service bool in multipart form: %s", err.Error())}
}
decoded.SelfService = parsed
}
return &decoded, nil
}
@ -99,6 +109,7 @@ func uploadSoftwareInstallerEndpoint(ctx context.Context, request interface{}, s
PostInstallScript: req.PostInstallScript,
InstallerFile: ff,
Filename: req.File.Filename,
SelfService: req.SelfService,
}
if err := svc.UploadSoftwareInstaller(ctx, payload); err != nil {