cmd

package
v0.3.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Overview

The cmd package implements the interface for the magellan CLI. The files contained in this package only contains implementations for handling CLI arguments and passing them to functions within magellan's internal API.

Each CLI subcommand will have at least one corresponding internal file with an API routine that implements the command's functionality. The main API routine will usually be the first function defined in the fill.

For example:

cmd/scan.go    --> internal/scan.go ( magellan.ScanForAssets() )
cmd/collect.go --> internal/collect.go ( magellan.CollectAll() )
cmd/list.go    --> none (doesn't have API call since it's simple)
cmd/update.go  --> internal/update.go ( magellan.UpdateFirmware() )

Index

Constants

This section is empty.

Variables

View Source
var CollectCmd = &cobra.Command{
	Use: "collect",
	Example: `  # basic collect after scan without making a follow-up request
  magellan collect --cache ./assets.db --cacert ochami.pem -o nodes.yaml -t 30

  # set username and password for all nodes and produce the collected
  # data in a file called 'nodes.yaml'
  magellan collect -u $bmc_username -p $bmc_password -o nodes.yaml

  # run a collect using secrets from the secrets manager
  export MASTER_KEY=$(magellan secrets generatekey)
  magellan secrets store $node_creds_json -f nodes.json
  magellan collect -o nodes.yaml

  # Take the output of 'scan' and input directly into 'collect'
  magellan scan --subnet 172.18.0.0/24 --port 5000 -l info -i -F json | magellan collect -f json --show-output -i

  # Similar to above, but with intermediate step to allow editting 'scan' output using YAML
  magellan scan --subnet 172.18.0.0/24 --port 5000 -l info -i -F yaml > asset.yaml
  magellan collect -d@asset.yaml -f yaml --show-output -i

  # Take the output of 'collect' and input directly into 'send'
  magellan collect -F json -i --show-output | magellan send https://demo.openchami.cluster:8443/hsm/v2
  
  # Complete flow combined as a single line (data format must match all commands)
  magellan scan --subnet 172.18.0.0/24 --port 5000 -l info -i -F json | magellan collect -f json -F json --show-output -i | magellan send -f json https://demo.openchami.cluster:8443/hsm/v2

  # Run 'collect' using environment variables
  SHOW_OUTPUT=true LOG_LEVEL=debug magellan collect -i -d@assets.json
  `,
	Short: "Collect hardware inventory by interrogating BMC nodes using scan data",
	Long: `	Collect hardware inventory by interrogating BMC nodes using scan data. This command send request(s) to a collection of hosts running Redfish services found stored from the 'scan' in cache, provided through stdin, or provided using the '-d/--data' flag. 
	
	See the 'scan' command on how to perform a scan to create. 
	
	The path to BMC ID mappings can be specified using the '--bmc-id-mappings' flag. This will convert any hosts found  
	
	See 'magellan-collect(1)' for more details. See 'magellan(1)' for a list of available environment variables.
	`,
	Run: func(cmd *cobra.Command, args []string) {
		// get probe states stored in db from scan
		var (
			scannedResults []magellan.RemoteAsset
			isStdinEmpty   bool

			// used for processing stdin and --data arguments
			inputData []map[string]any
			temp      = processDataArgs(collectDataArgs, collectInputFormat)
			err       error
		)

		isStdinEmpty, err = IsStdinEmpty()
		if err != nil {
			log.Warn().Err(err).Msg("failed to determine if stdin is empty")
		}
		log.Debug().
			Str("cache", cachePath).
			Bool("is_stdin_empty", isStdinEmpty).
			Send()
		if isStdinEmpty {
			if cachePath == "" {
				log.Warn().Msg("expected '--cache' to be set when stdin is empty")
			}
			scannedResults, err = sqlite.GetScannedAssets(cachePath)
			if err != nil {
				log.Warn().Err(err).Msgf("failed to get scanned results from cache")
			}
		} else {

			for _, arg := range args {
				var asset magellan.RemoteAsset
				err = format.UnmarshalData([]byte(arg), &asset, collectInputFormat)
				if err != nil {
					log.Warn().Err(err).Msg("failed to unmarshal data from standard input")
					continue
				}
				scannedResults = append(scannedResults, asset)
			}

			temp = append(temp, handleArgs(args, collectInputFormat)...)
		}

		for _, data := range temp {
			if data != nil {
				inputData = append(inputData, data)
			}
		}

		log.Debug().Int("input_count", len(inputData)).Send()

		for _, dataObject := range inputData {
			// assert that we have certain values in data object
			var (
				asset    magellan.RemoteAsset
				inputRaw []byte
			)
			inputRaw, err = format.MarshalData(dataObject, collectInputFormat)
			if err != nil {
				log.Error().Err(err).Msg("failed to marshal input data")
			}
			err = format.UnmarshalData(inputRaw, &asset, collectInputFormat)
			if err != nil {
				log.Error().Err(err).Msg("failed to unmarshal input data")
			}
			scannedResults = append(scannedResults, asset)
		}

		if len(scannedResults) == 0 {
			log.Error().Msg("data required to perform collect either from standard input, '--data' flag, or '--cache'")
			os.Exit(1)
		}

		if concurrency <= 0 {
			concurrency = mathutil.Clamp(len(scannedResults), 1, 10000)
		}

		// use secret store for BMC credentials, and/or credential CLI flags
		var store secrets.SecretStore
		if username != "" && password != "" {

			log.Debug().Msgf("--username and --password specified, using them for BMC credentials")
			store = secrets.NewStaticStore(username, password)
		} else {

			log.Debug().Msgf("one or both of --username and --password NOT passed, attempting to obtain missing credentials from secret store at %s", secretsFile)
			if store, err = secrets.OpenStore(secretsFile); err != nil {
				log.Error().Err(err).Msg("failed to open local secrets store")
			}

			if username != "" {
				log.Info().Msg("--username passed, temporarily overriding all usernames from secret store with value")
			}
			if password != "" {
				log.Info().Msg("--password passed, temporarily overriding all passwords from secret store with value")
			}
			switch s := store.(type) {
			case *secrets.StaticStore:
				if username != "" {
					s.Username = username
				}
				if password != "" {
					s.Password = password
				}
			case *secrets.LocalSecretStore:
				for k := range s.Secrets {
					if creds, err := bmc.GetBMCCredentials(store, k); err != nil {
						log.Error().Str("id", k).Err(err).Msg("failed to override BMC credentials")
					} else {
						if username != "" {
							creds.Username = username
						}
						if password != "" {
							creds.Password = password
						}

						if newCreds, err := json.Marshal(creds); err != nil {
							log.Error().Str("id", k).Err(err).Msg("failed to override BMC credentials: marshal error")
						} else {
							err = s.StoreSecretByID(k, string(newCreds))
							if err != nil {
								log.Error().Err(err).Str("id", k).Msg("failed to store secret by ID")
							}
						}
					}
				}
			}
		}

		params := &magellan.CollectParams{
			Timeout:      timeout,
			Concurrency:  concurrency,
			OutputPath:   outputPath,
			OutputDir:    outputDir,
			Insecure:     insecure,
			OutputFormat: collectOutputFormat,
			InputFormat:  collectInputFormat,
			SecretStore:  store,
			BMCIDMap:     idMap,
		}

		log.Debug().Any("params", params).Send()

		inventory, err := magellan.CollectInventory(&scannedResults, params)
		if err != nil {
			log.Error().Err(err).Msg("failed to collect data")
		}

		if showOutput {
			output, err := format.MarshalData(inventory, collectOutputFormat)
			if err != nil {
				log.Error().Msgf("failed to marshal inventory to %s", strings.ToUpper(collectOutputFormat.String()))
				os.Exit(1)
			}
			fmt.Println(string(output))
		}
	},
}

The `collect` command fetches data from a collection of BMC nodes. This command should be ran after the `scan` to find available hosts on a subnet.

View Source
var CrawlCmd = &cobra.Command{
	Use: "crawl <bmc_host>",
	Example: `  # crawl a single BMC node for hardware inventory
  magellan crawl http://nid0001
  
  # crawl BMC node with specified username and password
  magellan crawl https://bmc.example.com -i -u $username -p $password
  
  # crawl BMC node using secrets store with credentials
  magellan secrets generatekey -o key.txt
  export MASTER_KEY=$(cat key.txt)
  magellan secrets store $username $password
  magellan crawl --secrets-file secrets.json
  `,
	Short: "Crawl a single BMC for inventory information",
	Long:  "Crawl a single BMC for inventory information with URI.\n\n NOTE: This command does not scan subnets, store scan information in cache, nor make a request to a specified host. It is used only to retrieve inventory data directly. Otherwise, use 'scan' and 'collect' instead.",
	Args: func(cmd *cobra.Command, args []string) error {
		// Validate that the only argument is a valid URI
		var err error
		if err := cobra.ExactArgs(1)(cmd, args); err != nil {
			return err
		}
		args[0], err = urlx.Sanitize(args[0])
		if err != nil {
			return fmt.Errorf("failed to sanitize URI: %w", err)
		}
		return nil
	},
	Run: func(cmd *cobra.Command, args []string) {
		var (
			uri    = args[0]
			store  secrets.SecretStore
			output []byte
			err    error
		)

		if username != "" && password != "" {

			log.Debug().Str("uri", uri).Msgf("--username and --password specified, using them for BMC credentials")
			store = secrets.NewStaticStore(username, password)
		} else {

			log.Debug().Str("uri", uri).Msgf("one or both of --username and --password NOT passed, attempting to obtain missing credentials from secret store at %s", secretsFile)
			if store, err = secrets.OpenStore(secretsFile); err != nil {
				log.Error().Str("uri", uri).Err(err).Msg("failed to open local secrets store")
				return
			}

			// Either none of the flags were passed or only one of them were; get
			// credentials from secrets store to fill in the gaps.
			//
			// Attempt to get URI-specific credentials.
			var nodeCreds secrets.StaticStore
			if uriCreds, err := store.GetSecretByID(uri); err != nil {

				log.Warn().Str("uri", uri).Msg("specific credentials not found, falling back to default")
				defaultSecret, err := store.GetSecretByID(secrets.DEFAULT_KEY)
				if err != nil {

					log.Warn().Str("uri", uri).Err(err).Msg("no default credentials were set, they will be blank unless overridden by CLI flags")
				} else {
					// Default credentials found, use them.
					var creds bmc.BMCCredentials
					if err = json.Unmarshal([]byte(defaultSecret), &creds); err != nil {
						log.Warn().Str("uri", uri).Err(err).Msg("failed to unmarshal default secrets store credentials")
					} else {
						log.Info().Str("uri", uri).Msg("default credentials found, using")
						nodeCreds.Username = creds.Username
						nodeCreds.Password = creds.Password
					}
				}
			} else {
				// Specific URI credentials found, use them.
				var creds bmc.BMCCredentials
				if err = json.Unmarshal([]byte(uriCreds), &creds); err != nil {
					log.Warn().Str("uri", uri).Err(err).Msg("failed to unmarshal uri credentials")
				} else {
					nodeCreds.Username = creds.Username
					nodeCreds.Password = creds.Password
					log.Info().Str("uri", uri).Msg("specific credentials found, using")
				}
			}

			if username != "" {
				log.Info().Str("uri", uri).Msg("--username was set, overriding username for this BMC")
				nodeCreds.Username = username
			}
			if password != "" {
				log.Info().Str("uri", uri).Msg("--password was set, overriding password for this BMC")
				nodeCreds.Password = password
			}

			store = &nodeCreds
		}

		var (
			systems  []crawler.InventoryDetail
			managers []crawler.Manager
			config   = crawler.CrawlerConfig{
				URI:             uri,
				CredentialStore: store,
				Insecure:        insecure,
				UseDefault:      true,
			}
		)

		systems, err = crawler.CrawlBMCForSystems(config)
		if err != nil {
			log.Error().Err(err).Msg("failed to crawl BMC for systems")
		}
		managers, err = crawler.CrawlBMCForManagers(config)
		if err != nil {
			log.Error().Err(err).Msg("failed to crawl BMC for managers")
		}

		output, err = format.MarshalData(map[string]any{
			"Systems":  systems,
			"Managers": managers,
		}, crawlOutputFormat)
		if err != nil {
			log.Error().Err(err).Msg("failed to marshal output JSON")
			return
		}
		if showOutput {
			fmt.Println(string(output))
		}
	},
}

The `crawl` command walks a collection of Redfish endpoints to collect specfic inventory detail. This command only expects host names and does not require a scan to be performed beforehand.

View Source
var (
	Insecure bool
)
View Source
var ListCmd = &cobra.Command{
	Use: "list",
	Example: `  magellan list
  magellan list --cache ./assets.db
  magellan list --cache-info
	`,
	Args:  cobra.ExactArgs(0),
	Short: "List information stored in cache from a scan",
	Long: "Prints all of the host and associated data found from performing a scan.\n" +
		"See the 'scan' command on how to perform a scan.",
	Run: func(cmd *cobra.Command, args []string) {

		if showCache {
			log.Info().Str("cache", cachePath).Send()
			return
		}

		scannedResults, err := sqlite.GetScannedAssets(cachePath)
		if err != nil {
			log.Error().Err(err).Str("path", cachePath).Msg("failed to get scanned assets")
		}

		switch listOutputFormat {
		case format.FORMAT_JSON, format.FORMAT_YAML:
			output, err := format.MarshalData(scannedResults, listOutputFormat)
			if err != nil {
				log.Error().Err(err).Msg("failed to marshal data")
				return
			}
			fmt.Print(string(output))
		case format.FORMAT_LIST:
			fallthrough
		default:
			var output string
			for _, scanned := range scannedResults {
				output += fmt.Sprintf("%s %s %v %s\n",
					scanned.Host,
					scanned.Protocol,
					scanned.Timestamp,
					scanned.ServiceType,
				)
			}
			fmt.Print(output)
		}
	},
}

The `list` command provides an easy way to show what was found and stored in a cache database from a scan. The data that's stored is what is consumed by the `collect` command with the --cache flag.

View Source
var PowerCmd = &cobra.Command{
	Use: "power <node-id>...",
	Example: `  // get power state
  magellan power x1000c0s0b3n0
  // perform a particular type of reset
  magellan power x1000c0s0b3n0 -r On
  magellan power x1000c0s0b3n0 -r PowerCycle
  // list supported reset types
  magellan power x1000c0s0b3n0 -l
  // more realistic usage
  magellan power -u USER -p PASS -f collect.json x1000c0s0b3n0 x1000c0s0b3n1 x1000c0s0b3n2
  // inventory from stdin
  magellan collect -v ... | magellan power -f - x1000c0s0b3n0`,
	Short: "Get and set node power states",
	Long:  "Determine and control the power states of nodes found by a previous inventory crawl.\nSee the 'scan' and 'crawl' commands for further details.",
	Run: func(cmd *cobra.Command, args []string) {
		// Read node inventory from CLI flag, or default `collect` YAML output
		var datafile string
		if viper.IsSet("inventory-file") {
			datafile = viper.GetString("inventory-file")
		} else {
			datafile = viper.GetString("collect.output-file")
			log.Info().Msgf("parsing default inventory file from 'collect': %s", datafile)
		}

		nodes, err := power.ParseInventory(datafile, powerFormat)
		if err != nil {
			log.Fatal().Err(err).Msgf("failed to parse inventory file %s", datafile)

		}

		if concurrency <= 0 {
			concurrency = mathutil.Clamp(len(args), 1, 10000)
		}

		// Use secret store for BMC credentials, and/or credential CLI flags
		var store secrets.SecretStore
		if username != "" && password != "" {

			log.Debug().Msgf("--username and --password specified, using them for BMC credentials")
			store = secrets.NewStaticStore(username, password)
		} else {

			log.Debug().Msgf("one or both of --username and --password NOT passed, attempting to obtain missing credentials from secret store at %s", secretsFile)
			if store, err = secrets.OpenStore(secretsFile); err != nil {
				log.Error().Err(err).Msg("failed to open local secrets store")
			}

			if username != "" {
				log.Info().Msg("--username passed, temporarily overriding all usernames from secret store with value")
			}
			if password != "" {
				log.Info().Msg("--password passed, temporarily overriding all passwords from secret store with value")
			}
			switch s := store.(type) {
			case *secrets.StaticStore:
				if username != "" {
					s.Username = username
				}
				if password != "" {
					s.Password = password
				}
			case *secrets.LocalSecretStore:
				for k := range s.Secrets {
					if creds, err := bmc.GetBMCCredentials(store, k); err != nil {
						log.Error().Str("id", k).Err(err).Msg("failed to override BMC credentials")
					} else {
						if username != "" {
							creds.Username = username
						}
						if password != "" {
							creds.Password = password
						}

						if newCreds, err := json.Marshal(creds); err != nil {
							log.Error().Str("id", k).Err(err).Msg("failed to override BMC credentials: marshal error")
						} else {
							err = s.StoreSecretByID(k, string(newCreds))
							if err != nil {
								log.Error().Err(err).Str("id", k).Msg("failed to store secret by ID")
							}
						}
					}
				}
			}
		}

		nodemap := make(map[string]bmc.Node, len(nodes))
		for i := range nodes {
			nodemap[nodes[i].ClusterID] = nodes[i]
		}

		target_nodes := make([]power.CrawlableNode, 0, len(args))
		for i := range args {
			node, found := nodemap[args[i]]
			if !found {
				log.Error().Msgf("target node '%s' not found in inventory; skipping", args[i])
				continue
			}
			target_nodes = append(target_nodes, power.CrawlableNode{
				ClusterID: node.ClusterID,
				NodeID:    node.NodeID,
				ConnConfig: crawler.CrawlerConfig{
					URI:             "https://" + node.BmcIP,
					CredentialStore: store,
					Insecure:        insecure,
				},
			})
		}

		// Create the appropriate "action function" based on CLI flags (or lack thereof)
		var action_func func(power.CrawlableNode) string
		if list_reset_types {
			action_func = func(target power.CrawlableNode) string {
				types, err := power.GetResetTypes(target)
				if err != nil {
					log.Error().Err(err).Msgf("failed to get reset types for node %s", target.ClusterID)
					return ""
				}
				return fmt.Sprintf("%s", types)
			}
		} else if reset_type != "" {
			action_func = func(target power.CrawlableNode) string {

				_, err := power.ResetComputerSystem(target, schemas.ResetType(reset_type))
				if err != nil {
					log.Error().Err(err).Msgf("failed to reset node %s", target.ClusterID)
					return "failure"
				}
				return "success"
			}
		} else {
			action_func = func(target power.CrawlableNode) string {
				state, err := power.GetPowerState(target)
				if err != nil {
					log.Error().Err(err).Msgf("failed to get power state of node %s", target.ClusterID)
					state = "unknown"
				}
				return string(state)
			}
		}

		results := concurrent_helper(concurrency, target_nodes, action_func)
		power.LogoutBMCSessions()
		for node, status := range results {
			fmt.Printf("%s:\t%s\n", node, status)
		}
	},
}

The `power` command gets and sets power states for a collection of BMC nodes. This command should be run after `collect`, as it requires an existing node inventory.

View Source
var ScanCmd = &cobra.Command{
	Use: "scan",
	Example: `  # assumes host https://10.0.0.101:443
  magellan scan 10.0.0.101 --insecure

  # assumes subnet using HTTPS and port 443 except for specified host
  magellan scan http://10.0.0.101:80 https://$username:$password@10.0.0.102:443 http://172.16.0.105:8080 --subnet 172.16.0.0/24

  # assumes hosts http://10.0.0.101:8080 and http://10.0.0.102:8080
  magellan scan 10.0.0.101 10.0.0.102 https://172.16.0.10:443 --port 8080 --protocol tcp

  # assumes subnet using default unspecified subnet-masks
  magellan scan --subnet 10.0.0.0 -i

  # assumes subnet using HTTPS and port 443 with specified CIDR
  magellan scan --subnet 10.0.0.0/16 -i

  # same as above example but output is in JSON without caching
  magellan scan --subnet 10.0.0.0/16 -i -F json --disable-cache

  # assumes subnet using HTTP and port 5000 similar to 192.168.0.0/16
  magellan scan --subnet 192.168.0.0 --protocol tcp --scheme https --port 5000 --subnet-mask 255.255.0.0

  # assumes subnet without CIDR has a subnet-mask of 255.255.0.0
  magellan scan --subnet 10.0.0.0 --subnet 172.16.0.0 --subnet-mask 255.255.0.0 --cache ./assets.db

  # pipe 'scan' output as input for 'scan' (requires that 'collect' accept the same data format as 'scan')
  magellan secrets generatekey -f key.txt
  export MASTER_KEY=$(cat key.txt)
  magellan secrets store default $username:$password -f secrets.json
  magellan scan --subnet 172.18.0.0/24 --port 5000 -i -F yaml | magellan collect -i -f yaml --secrets-file secrets.json
  
  // perform scan by setting environment variables
  SCAN_SUBNET=172.18.0.0/24 SCAN_PORTS=5000 magellan scan -l info --insecure`,
	Short: "Perform network scan to discover BMC nodes",
	Long: `Perform a network scan by attempting to connect to each host and port specified 
and getting a response. Each host is passed *with a full URL* including the 
protocol and port. Additional subnets can be added by using the '--subnet' flag 
and providing an IP address on the subnet as well as a CIDR. If no CIDR is 
provided, then the subnet mask specified with the '--subnet-mask' flag will be 
used instead (will use default mask if not set).

Similarly, any host provided with no port will use either the ports specified 
with '--port' or the default port used with each specified protocol. The default
protocol is 'tcp' unless specified. The '--scheme' flag works similarly and the
default value is 'https' in the host URL or with the '--protocol' flag.

If the '--disable-probe' flag is used, the tool will not send another request to
probe for available. Redfish and JAWS services. This is not recommended, since 
the extra request makes the scan a bit more reliable for determining which hosts
to collect inventory data.

The output of the 'scan' can be piped as input to 'collect', but both commands 
must use the same data format specified with '-f/--input-format' and 
'-F/--output-format'.

See 'magellan-scan(1)' for more details. See 'magellan(1)' for a list of available environment variables.
	`,

	Run: func(cmd *cobra.Command, args []string) {

		if len(ports) == 0 {
			ports = magellan.GetDefaultPorts()
			log.Debug().Ints("ports", ports).Msg("default ports")
		}

		targetHosts = append(targetHosts, urlx.FormatHosts(args, ports, scheme)...)

		for _, subnet := range subnets {

			subnetHosts := magellan.GenerateHostsWithSubnet(subnet, &subnetMask, ports, scheme)
			targetHosts = append(targetHosts, subnetHosts...)
		}

		if len(targetHosts) <= 0 {
			log.Error().Msg("nothing to do (no valid target hosts)")
			os.Exit(1)
		} else {
			if len(targetHosts[0]) <= 0 {
				log.Error().Msg("nothing to do (no valid target hosts)")
				os.Exit(1)
			}
		}

		combinedTargetHosts := []string{}
		for _, targetHost := range targetHosts {
			combinedTargetHosts = append(combinedTargetHosts, targetHost...)
		}
		log.Debug().Any("flags", map[string]any{
			"hosts":           "set '--log-level' to 'trace' to show",
			"cache":           cachePath,
			"concurrency":     concurrency,
			"protocol":        protocol,
			"subnets":         subnets,
			"subnet-mask":     subnetMask.String(),
			"cert":            cacertPath,
			"disable-probing": disableProbing,
			"disable-caching": disableCache,
		}).Send()

		if concurrency <= 0 {
			concurrency = len(targetHosts)
		} else {
			concurrency = mathutil.Clamp(len(targetHosts), 1, len(targetHosts))
		}

		foundAssets := magellan.ScanForAssets(&magellan.ScanParams{
			TargetHosts:    targetHosts,
			Scheme:         scheme,
			Protocol:       protocol,
			Concurrency:    concurrency,
			Timeout:        timeout,
			DisableProbing: disableProbing,
			Insecure:       insecure,
			Include:        include,
		})

		if len(foundAssets) > 0 {
			log.Trace().Any("assets", foundAssets).Msgf("found assets from scan")
		} else {
			log.Warn().Msg("no responsive assets found")
			return
		}

		if scanFormat != "" {
			switch scanFormat {
			case format.FORMAT_JSON, format.FORMAT_YAML:
				var (
					output []byte
					err    error
				)

				output, err = format.MarshalData(foundAssets, scanFormat)
				if err != nil {
					log.Error().Err(err).Msgf("failed to marshal output to %s", scanFormat)
					return
				}
				if outputPath != "" {
					err := os.WriteFile(outputPath, output, 0644)
					if err != nil {
						log.Error().Err(err).Msgf("failed to write to file: %s", outputPath)
					} else {
						log.Debug().Msgf("scan results written to %s", outputPath)
					}
				} else {
					fmt.Println(string(output))
				}

			default:
				log.Error().Msgf("unknown format specified: %s. Please use 'db', 'json', or 'yaml'.", scanFormat)
			}
		}

		if !disableCache && cachePath != "" {
			err := os.MkdirAll(path.Dir(cachePath), 0755)
			if err != nil {
				log.Error().Err(err).Msg("failed to make cache directory")
			}
			err = sqlite.InsertScannedAssets(cachePath, foundAssets...)
			if err != nil {
				log.Error().Err(err).Msg("failed to write scanned assets to cache")
			}
			log.Debug().Str("path", cachePath).Msg("saved assets to cache")
		}
	},
}

The `scan` command is usually the first step to using the CLI tool. This command will perform a network scan over a subnet by supplying a list of subnets, subnet masks, and additional IP address to probe.

See the `ScanForAssets()` function in 'internal/scan.go' for details related to the implementation.

Functions

func Execute

func Execute()

This Execute() function is called from main to run the CLI.

func InitializeConfig added in v0.0.7

func InitializeConfig()

InitializeConfig() initializes a new config object by loading it from a file given a non-empty string.

func InitializeLogger added in v0.3.12

func InitializeLogger()

func IsStdinEmpty added in v0.3.12

func IsStdinEmpty() (bool, error)

func ReadStdin added in v0.3.0

func ReadStdin() ([]byte, error)

ReadStdin reads all of standard input and returns the bytes. If an error occurs during scanning, it is returned.

func SetDefaults added in v0.0.7

func SetDefaults()

SetDefaults() resets all of the viper properties back to their default values.

TODO: This function should probably be moved to 'internal/config.go' instead of in this file.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL