cscli machines: lint + write output to stdout instead of log (#2657)

* feedback on stdout, not log.Info
* rename parameters to silence warnings from "unusedparams"
* debian postinst: skip duplicate warnings with 'cscli machines add'
* rpm postinst: skip duplicate warnings in 'cscli machines add'
* update func tests
* debian prerm: if dashboard remove fails, explain it's ok
* debian prerm: suppress warnings about wal, capi when attempting to remove the dashboard
* wizard.sh: log format like crowdsec
This commit is contained in:
mmetc 2023-12-13 15:43:46 +01:00 committed by GitHub
parent 6eabb461ce
commit 12d9fba4b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 39 additions and 39 deletions

View file

@ -8,7 +8,6 @@ import (
"io" "io"
"math/big" "math/big"
"os" "os"
"slices"
"strings" "strings"
"time" "time"
@ -18,16 +17,16 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v3"
"slices"
"github.com/crowdsecurity/machineid" "github.com/crowdsecurity/machineid"
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
"github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/database" "github.com/crowdsecurity/crowdsec/pkg/database"
"github.com/crowdsecurity/crowdsec/pkg/database/ent" "github.com/crowdsecurity/crowdsec/pkg/database/ent"
"github.com/crowdsecurity/crowdsec/pkg/types" "github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require"
) )
const passwordLength = 64 const passwordLength = 64
@ -63,9 +62,9 @@ func generateIDPrefix() (string, error) {
} }
log.Debugf("failed to get machine-id with usual files: %s", err) log.Debugf("failed to get machine-id with usual files: %s", err)
bId, err := uuid.NewRandom() bID, err := uuid.NewRandom()
if err == nil { if err == nil {
return bId.String(), nil return bID.String(), nil
} }
return "", fmt.Errorf("generating machine id: %w", err) return "", fmt.Errorf("generating machine id: %w", err)
} }
@ -107,27 +106,27 @@ func getAgents(out io.Writer, dbClient *database.Client) error {
if err != nil { if err != nil {
return fmt.Errorf("unable to list machines: %s", err) return fmt.Errorf("unable to list machines: %s", err)
} }
if csConfig.Cscli.Output == "human" {
switch csConfig.Cscli.Output {
case "human":
getAgentsTable(out, machines) getAgentsTable(out, machines)
} else if csConfig.Cscli.Output == "json" { case "json":
enc := json.NewEncoder(out) enc := json.NewEncoder(out)
enc.SetIndent("", " ") enc.SetIndent("", " ")
if err := enc.Encode(machines); err != nil { if err := enc.Encode(machines); err != nil {
return fmt.Errorf("failed to marshal") return fmt.Errorf("failed to marshal")
} }
return nil return nil
} else if csConfig.Cscli.Output == "raw" { case "raw":
csvwriter := csv.NewWriter(out) csvwriter := csv.NewWriter(out)
err := csvwriter.Write([]string{"machine_id", "ip_address", "updated_at", "validated", "version", "auth_type", "last_heartbeat"}) err := csvwriter.Write([]string{"machine_id", "ip_address", "updated_at", "validated", "version", "auth_type", "last_heartbeat"})
if err != nil { if err != nil {
return fmt.Errorf("failed to write header: %s", err) return fmt.Errorf("failed to write header: %s", err)
} }
for _, m := range machines { for _, m := range machines {
var validated string validated := "false"
if m.IsValidated { if m.IsValidated {
validated = "true" validated = "true"
} else {
validated = "false"
} }
hb, _ := getLastHeartbeat(m) hb, _ := getLastHeartbeat(m)
err := csvwriter.Write([]string{m.MachineId, m.IpAddress, m.UpdatedAt.Format(time.RFC3339), validated, m.Version, m.AuthType, hb}) err := csvwriter.Write([]string{m.MachineId, m.IpAddress, m.UpdatedAt.Format(time.RFC3339), validated, m.Version, m.AuthType, hb})
@ -136,13 +135,13 @@ func getAgents(out io.Writer, dbClient *database.Client) error {
} }
} }
csvwriter.Flush() csvwriter.Flush()
} else { default:
log.Errorf("unknown output '%s'", csConfig.Cscli.Output) return fmt.Errorf("unknown output '%s'", csConfig.Cscli.Output)
} }
return nil return nil
} }
type cliMachines struct {} type cliMachines struct{}
func NewCLIMachines() *cliMachines { func NewCLIMachines() *cliMachines {
return &cliMachines{} return &cliMachines{}
@ -158,9 +157,9 @@ Note: This command requires database direct access, so is intended to be run on
Example: `cscli machines [action]`, Example: `cscli machines [action]`,
DisableAutoGenTag: true, DisableAutoGenTag: true,
Aliases: []string{"machine"}, Aliases: []string{"machine"},
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
var err error var err error
if err := require.LAPI(csConfig); err != nil { if err = require.LAPI(csConfig); err != nil {
return err return err
} }
dbClient, err = database.NewClient(csConfig.DbConfig) dbClient, err = database.NewClient(csConfig.DbConfig)
@ -188,7 +187,7 @@ func (cli cliMachines) NewListCmd() *cobra.Command {
Example: `cscli machines list`, Example: `cscli machines list`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, _ []string) error {
err := getAgents(color.Output, dbClient) err := getAgents(color.Output, dbClient)
if err != nil { if err != nil {
return fmt.Errorf("unable to list machines: %s", err) return fmt.Errorf("unable to list machines: %s", err)
@ -279,7 +278,8 @@ func (cli cliMachines) add(cmd *cobra.Command, args []string) error {
if dumpFile == "" && csConfig.API.Client != nil && csConfig.API.Client.CredentialsFilePath != "" { if dumpFile == "" && csConfig.API.Client != nil && csConfig.API.Client.CredentialsFilePath != "" {
credFile := csConfig.API.Client.CredentialsFilePath credFile := csConfig.API.Client.CredentialsFilePath
// use the default only if the file does not exist // use the default only if the file does not exist
_, err := os.Stat(credFile) _, err = os.Stat(credFile)
switch { switch {
case os.IsNotExist(err) || force: case os.IsNotExist(err) || force:
dumpFile = csConfig.API.Client.CredentialsFilePath dumpFile = csConfig.API.Client.CredentialsFilePath
@ -311,7 +311,7 @@ func (cli cliMachines) add(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return fmt.Errorf("unable to create machine: %s", err) return fmt.Errorf("unable to create machine: %s", err)
} }
log.Infof("Machine '%s' successfully added to the local API", machineID) fmt.Printf("Machine '%s' successfully added to the local API.\n", machineID)
if apiURL == "" { if apiURL == "" {
if csConfig.API.Client != nil && csConfig.API.Client.Credentials != nil && csConfig.API.Client.Credentials.URL != "" { if csConfig.API.Client != nil && csConfig.API.Client.Credentials != nil && csConfig.API.Client.Credentials.URL != "" {
@ -336,7 +336,7 @@ func (cli cliMachines) add(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return fmt.Errorf("write api credentials in '%s' failed: %s", dumpFile, err) return fmt.Errorf("write api credentials in '%s' failed: %s", dumpFile, err)
} }
log.Printf("API credentials written to '%s'", dumpFile) fmt.Printf("API credentials written to '%s'.\n", dumpFile)
} else { } else {
fmt.Printf("%s\n", string(apiConfigDump)) fmt.Printf("%s\n", string(apiConfigDump))
} }
@ -352,7 +352,7 @@ func (cli cliMachines) NewDeleteCmd() *cobra.Command {
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
Aliases: []string{"remove"}, Aliases: []string{"remove"},
DisableAutoGenTag: true, DisableAutoGenTag: true,
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
machines, err := dbClient.ListMachines() machines, err := dbClient.ListMachines()
if err != nil { if err != nil {
cobra.CompError("unable to list machines " + err.Error()) cobra.CompError("unable to list machines " + err.Error())
@ -371,7 +371,7 @@ func (cli cliMachines) NewDeleteCmd() *cobra.Command {
return cmd return cmd
} }
func (cli cliMachines) delete(cmd *cobra.Command, args []string) error { func (cli cliMachines) delete(_ *cobra.Command, args []string) error {
for _, machineID := range args { for _, machineID := range args {
err := dbClient.DeleteWatcher(machineID) err := dbClient.DeleteWatcher(machineID)
if err != nil { if err != nil {
@ -395,7 +395,7 @@ cscli machines prune --duration 1h
cscli machines prune --not-validated-only --force`, cscli machines prune --not-validated-only --force`,
Args: cobra.NoArgs, Args: cobra.NoArgs,
DisableAutoGenTag: true, DisableAutoGenTag: true,
PreRunE: func(cmd *cobra.Command, args []string) error { PreRunE: func(cmd *cobra.Command, _ []string) error {
dur, _ := cmd.Flags().GetString("duration") dur, _ := cmd.Flags().GetString("duration")
var err error var err error
parsedDuration, err = time.ParseDuration(fmt.Sprintf("-%s", dur)) parsedDuration, err = time.ParseDuration(fmt.Sprintf("-%s", dur))
@ -404,7 +404,7 @@ cscli machines prune --not-validated-only --force`,
} }
return nil return nil
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, _ []string) error {
notValidOnly, _ := cmd.Flags().GetBool("not-validated-only") notValidOnly, _ := cmd.Flags().GetBool("not-validated-only")
force, _ := cmd.Flags().GetBool("force") force, _ := cmd.Flags().GetBool("force")
if parsedDuration >= 0-60*time.Second && !notValidOnly { if parsedDuration >= 0-60*time.Second && !notValidOnly {
@ -472,7 +472,7 @@ func (cli cliMachines) NewValidateCmd() *cobra.Command {
Example: `cscli machines validate "machine_name"`, Example: `cscli machines validate "machine_name"`,
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
machineID := args[0] machineID := args[0]
if err := dbClient.ValidateMachine(machineID); err != nil { if err := dbClient.ValidateMachine(machineID); err != nil {
return fmt.Errorf("unable to validate machine '%s': %s", machineID, err) return fmt.Errorf("unable to validate machine '%s': %s", machineID, err)

4
debian/postinst vendored
View file

@ -58,7 +58,7 @@ if [ "$1" = configure ]; then
db_get crowdsec/capi db_get crowdsec/capi
CAPI=$RET CAPI=$RET
[ -s /etc/crowdsec/local_api_credentials.yaml ] || cscli machines add -a --force [ -s /etc/crowdsec/local_api_credentials.yaml ] || cscli machines add -a --force --error
if [ "$CAPI" = true ]; then if [ "$CAPI" = true ]; then
cscli capi register cscli capi register
@ -104,4 +104,4 @@ if [ "$1" = configure ]; then
fi fi
fi fi
echo "You can always run the configuration again interactively by using '/usr/share/crowdsec/wizard.sh -c" echo "You can always run the configuration again interactively by using '/usr/share/crowdsec/wizard.sh -c'"

2
debian/preinst vendored
View file

@ -40,4 +40,4 @@ if [ "$1" = upgrade ]; then
fi fi
fi fi
echo "You can always run the configuration again interactively by using '/usr/share/crowdsec/wizard.sh -c" echo "You can always run the configuration again interactively by using '/usr/share/crowdsec/wizard.sh -c'"

2
debian/prerm vendored
View file

@ -1,5 +1,5 @@
if [ "$1" = "remove" ]; then if [ "$1" = "remove" ]; then
cscli dashboard remove -f -y || true cscli dashboard remove -f -y --error || echo "Ignore the above error if you never installed the local dashboard."
systemctl stop crowdsec systemctl stop crowdsec
systemctl disable crowdsec systemctl disable crowdsec
fi fi

View file

@ -173,7 +173,7 @@ if [ $1 == 1 ]; then
fi fi
if [ ! -f "%{_sysconfdir}/crowdsec/local_api_credentials.yaml" ] ; then if [ ! -f "%{_sysconfdir}/crowdsec/local_api_credentials.yaml" ] ; then
install -m 600 /dev/null /etc/crowdsec/local_api_credentials.yaml install -m 600 /dev/null /etc/crowdsec/local_api_credentials.yaml
cscli machines add -a --force cscli machines add -a --force --error
fi fi
cscli hub update cscli hub update

View file

@ -34,13 +34,13 @@ teardown() {
rune -0 jq -r '.msg' <(stderr) rune -0 jq -r '.msg' <(stderr)
assert_output --partial 'already exists: please remove it, use "--force" or specify a different file with "-f"' assert_output --partial 'already exists: please remove it, use "--force" or specify a different file with "-f"'
rune -0 cscli machines add local -a --force rune -0 cscli machines add local -a --force
assert_stderr --partial "Machine 'local' successfully added to the local API" assert_output --partial "Machine 'local' successfully added to the local API."
} }
@test "add a new machine and delete it" { @test "add a new machine and delete it" {
rune -0 cscli machines add -a -f /dev/null CiTestMachine -o human rune -0 cscli machines add -a -f /dev/null CiTestMachine -o human
assert_stderr --partial "Machine 'CiTestMachine' successfully added to the local API" assert_output --partial "Machine 'CiTestMachine' successfully added to the local API"
assert_stderr --partial "API credentials written to '/dev/null'" assert_output --partial "API credentials written to '/dev/null'"
# we now have two machines # we now have two machines
rune -0 cscli machines list -o json rune -0 cscli machines list -o json

View file

@ -95,33 +95,33 @@ rm -rf -- "$BACKUP_DIR"
log_info() { log_info() {
msg=$1 msg=$1
date=$(date +%x:%X) date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${BLUE}INFO${NC}[${date}] crowdsec_wizard: ${msg}" echo -e "${BLUE}INFO${NC}[${date}] crowdsec_wizard: ${msg}"
} }
log_fatal() { log_fatal() {
msg=$1 msg=$1
date=$(date +%x:%X) date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${RED}FATA${NC}[${date}] crowdsec_wizard: ${msg}" 1>&2 echo -e "${RED}FATA${NC}[${date}] crowdsec_wizard: ${msg}" 1>&2
exit 1 exit 1
} }
log_warn() { log_warn() {
msg=$1 msg=$1
date=$(date +%x:%X) date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${ORANGE}WARN${NC}[${date}] crowdsec_wizard: ${msg}" echo -e "${ORANGE}WARN${NC}[${date}] crowdsec_wizard: ${msg}"
} }
log_err() { log_err() {
msg=$1 msg=$1
date=$(date +%x:%X) date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "${RED}ERR${NC}[${date}] crowdsec_wizard: ${msg}" 1>&2 echo -e "${RED}ERR${NC}[${date}] crowdsec_wizard: ${msg}" 1>&2
} }
log_dbg() { log_dbg() {
if [[ ${DEBUG_MODE} == "true" ]]; then if [[ ${DEBUG_MODE} == "true" ]]; then
msg=$1 msg=$1
date=$(date +%x:%X) date=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "[${date}][${YELLOW}DBG${NC}] crowdsec_wizard: ${msg}" 1>&2 echo -e "[${date}][${YELLOW}DBG${NC}] crowdsec_wizard: ${msg}" 1>&2
fi fi
} }