fleet/server/datastore/mysql/osquery_options.go
Zachary Wasserman 5e9fe9d5a1
Transition osquery options interfaces for compatibility with fleetctl (#1649)
- Refinements to options yaml definition
- Datastore and service implementations
- Migration to bring existing options into new table format
2017-12-13 18:14:54 -05:00

133 lines
3.5 KiB
Go

package mysql
import (
"database/sql"
"encoding/json"
"fmt"
"github.com/go-kit/kit/log/level"
"github.com/kolide/fleet/server/kolide"
"github.com/pkg/errors"
)
type optionsRow struct {
ID int `db:"id"`
OverrideType kolide.OptionOverrideType `db:"override_type"`
OverrideIdentifier string `db:"override_identifier"`
Options string `db:"options"`
}
func (d *Datastore) ApplyOptions(spec *kolide.OptionsSpec) (err error) {
tx, err := d.db.Begin()
if err != nil {
return errors.Wrap(err, "begin ApplyOptions transaction")
}
defer func() {
if err != nil {
rbErr := tx.Rollback()
// It seems possible that there might be a case in
// which the error we are dealing with here was thrown
// by the call to tx.Commit(), and the docs suggest
// this call would then result in sql.ErrTxDone.
if rbErr != nil && rbErr != sql.ErrTxDone {
panic(fmt.Sprintf("got err '%s' rolling back after err '%s'", rbErr, err))
}
}
}()
// Clear all the existing options
_, err = tx.Exec("DELETE FROM osquery_options")
if err != nil {
return errors.Wrap(err, "delete existing options")
}
// Save new options
sql := `
INSERT INTO osquery_options (
override_type, override_identifier, options
) VALUES (?, ?, ?)
`
// Default options
_, err = tx.Exec(sql, kolide.OptionOverrideTypeDefault, "", string(spec.Config))
if err != nil {
return errors.Wrap(err, "saving default config")
}
// Platform overrides
for platform, opts := range spec.Overrides.Platforms {
_, err = tx.Exec(sql, kolide.OptionOverrideTypePlatform, platform, string(opts))
if err != nil {
return errors.Wrapf(err, "saving %s platform config", platform)
}
}
// Success!
err = tx.Commit()
if err != nil {
return errors.Wrap(err, "commit ApplyOptions transaction")
}
return nil
}
func (d *Datastore) GetOptions() (*kolide.OptionsSpec, error) {
var rows []optionsRow
if err := d.db.Select(&rows, "SELECT * FROM osquery_options"); err != nil {
return nil, errors.Wrap(err, "selecting options")
}
spec := &kolide.OptionsSpec{
Overrides: kolide.OptionsOverrides{
Platforms: make(map[string]json.RawMessage),
},
}
for _, row := range rows {
switch row.OverrideType {
case kolide.OptionOverrideTypeDefault:
spec.Config = json.RawMessage(row.Options)
case kolide.OptionOverrideTypePlatform:
spec.Overrides.Platforms[row.OverrideIdentifier] = json.RawMessage(row.Options)
default:
level.Info(d.logger).Log(
"err", "ignoring unkown override type",
"type", row.OverrideType,
)
}
}
return spec, nil
}
func (d *Datastore) OptionsForPlatform(platform string) (json.RawMessage, error) {
// SQL uses a custom ordering function to return the single correct
// config with the highest precedence override (the FIELD function
// defines this ordering). If there is no override, it returns the
// default.
sql := `
SELECT * FROM osquery_options
WHERE override_type = ? OR
(override_type = ? AND override_identifier = ?)
ORDER BY FIELD(override_type, ?, ?)
LIMIT 1
`
var row optionsRow
err := d.db.Get(
&row, sql,
kolide.OptionOverrideTypeDefault,
kolide.OptionOverrideTypePlatform, platform,
// Order of the following arguments defines precedence of
// overrides.
kolide.OptionOverrideTypePlatform, kolide.OptionOverrideTypeDefault,
)
if err != nil {
return nil, errors.Wrapf(err, "retrieving osquery options for platform '%s'", platform)
}
return json.RawMessage(row.Options), nil
}