Disable AI features on non-new installations upgrading to 4.51.X (#19482)

#19365

Assuming we release this fix in 4.51.0:
- Migration from a version without the feature (< 4.50.0) to 4.51.0:
Should disable (set `ai_features_disabled=true`).
- Migration from a version with the feature (>= 4.50.X < 4.51.0) to
4.51.0: Should keep `ai_features_disabled` as-is.
- New installation of Fleet: Should come with AI features enabled
(`ai_features_disabled=false`).

From
https://github.com/fleetdm/fleet/issues/19365#issuecomment-2145825363.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [X] Manual QA for all new/changed functionality
This commit is contained in:
Lucas Manuel Rodriguez 2024-06-10 16:49:27 -03:00 committed by GitHub
parent 3e19cd90a9
commit 5f65ea831c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 65 additions and 0 deletions

View file

@ -0,0 +1 @@
* Disabled AI features on non-new installations upgrading from < 4.50.X to >= 4.51.X.

View file

@ -2,7 +2,10 @@ package tables
import (
"database/sql"
"encoding/json"
"fmt"
"github.com/pkg/errors"
)
func init() {
@ -22,6 +25,67 @@ func Up_20240430111727(tx *sql.Tx) error {
if err != nil {
return fmt.Errorf("failed to delete query_results %w", err)
}
//
// The following "fix" was introduced after this migration was released in 4.50.0.
// We are adding it here to disable the AI features (set ai_features_disabled=true)
// for non-new installations that are upgrading from < 4.50.0 using the new version
// of this migration to be released in 4.51.X.
//
if err := fixDisableAIForNonNewInstallation(tx); err != nil {
return fmt.Errorf("failed to update ai_features_disabled: %w", err)
}
return nil
}
func fixDisableAIForNonNewInstallation(tx *sql.Tx) error {
var usersCount int
row := tx.QueryRow(`SELECT COUNT(*) FROM users;`)
if err := row.Scan(&usersCount); err != nil {
return fmt.Errorf("select count users: %w", err)
}
if usersCount == 0 {
return nil
}
//
// At least a "setup" user was configured,
// thus we assume this is not a new installation.
//
var raw json.RawMessage
row = tx.QueryRow(`SELECT json_value FROM app_config_json LIMIT 1;`)
if err := row.Scan(&raw); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil
}
return fmt.Errorf("select app_config_json: %w", err)
}
var config map[string]interface{}
if err := json.Unmarshal(raw, &config); err != nil {
return fmt.Errorf("unmarshal appconfig: %w", err)
}
ss, ok := config["server_settings"]
if !ok {
return errors.New("missing server_settings")
}
serverSettings, ok := ss.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid type for server_settings: %T", ss)
}
serverSettings["ai_features_disabled"] = true
b, err := json.Marshal(config)
if err != nil {
return fmt.Errorf("marshal updated appconfig: %w", err)
}
if _, err := tx.Exec(`UPDATE app_config_json SET json_value = ? WHERE id = 1;`, b); err != nil {
return fmt.Errorf("update app_config_json: %w", err)
}
return nil
}