-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from anchore/initial-bootstrap
- Loading branch information
Showing
26 changed files
with
2,093 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/anchore/elastic-container-gatherer/ecg" | ||
"github.com/anchore/elastic-container-gatherer/internal/config" | ||
"github.com/anchore/elastic-container-gatherer/internal/logger" | ||
"github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
var appConfig *config.Application | ||
var log *logrus.Logger | ||
var cliOnlyOpts config.CliOnlyOptions | ||
|
||
func init() { | ||
setGlobalCliOptions() | ||
|
||
cobra.OnInitialize( | ||
InitAppConfig, | ||
initLogging, | ||
logAppConfig, | ||
) | ||
} | ||
|
||
func setGlobalCliOptions() { | ||
// setup global CLI options (available on all CLI commands) | ||
rootCmd.PersistentFlags().StringVarP(&cliOnlyOpts.ConfigPath, "config", "c", "", "application config file") | ||
|
||
flag := "quiet" | ||
rootCmd.PersistentFlags().BoolP( | ||
flag, "q", false, | ||
"suppress all logging output", | ||
) | ||
if err := viper.BindPFlag(flag, rootCmd.PersistentFlags().Lookup(flag)); err != nil { | ||
fmt.Printf("unable to bind flag '%s': %+v", flag, err) | ||
os.Exit(1) | ||
} | ||
|
||
rootCmd.PersistentFlags().CountVarP(&cliOnlyOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)") | ||
} | ||
|
||
func Execute() { | ||
if err := rootCmd.Execute(); err != nil { | ||
fmt.Fprintln(os.Stderr, err.Error()) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func InitAppConfig() { | ||
cfg, err := config.LoadConfigFromFile(viper.GetViper(), &cliOnlyOpts) | ||
if err != nil { | ||
fmt.Printf("failed to load application config: \n\t%+v\n", err) | ||
os.Exit(1) | ||
} | ||
appConfig = cfg | ||
} | ||
|
||
func GetAppConfig() *config.Application { | ||
return appConfig | ||
} | ||
|
||
func initLogging() { | ||
cfg := logger.LogrusConfig{ | ||
EnableConsole: (appConfig.Log.FileLocation == "" || appConfig.CliOptions.Verbosity > 0) && !appConfig.Quiet, | ||
EnableFile: appConfig.Log.FileLocation != "", | ||
Level: appConfig.Log.LevelOpt, | ||
Structured: appConfig.Log.Structured, | ||
FileLocation: appConfig.Log.FileLocation, | ||
} | ||
|
||
logWrapper := logger.NewLogrusLogger(cfg) | ||
log = logWrapper.Logger | ||
ecg.SetLogger(logWrapper) | ||
} | ||
|
||
func logAppConfig() { | ||
log.Debugf("Application config:\n%s", appConfig) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package cmd | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
// completionCmd represents the completion command | ||
var completionCmd = &cobra.Command{ | ||
Use: "completion [bash|zsh|fish]", | ||
Short: "Generate Completion script", | ||
Long: `To load completions: | ||
Bash: | ||
$ source <(ecg completion bash) | ||
# To load completions for each session, execute once: | ||
Linux: | ||
$ ecg completion bash > /etc/bash_completion.d/ecg | ||
MacOS: | ||
$ ecg completion bash > /usr/local/etc/bash_completion.d/ecg | ||
Zsh: | ||
# If shell completion is not already enabled in your environment you will need | ||
# to enable it. You can execute the following once: | ||
$ echo "autoload -U compinit; compinit" >> ~/.zshrc | ||
# To load completions for each session, execute once: | ||
$ ecg completion zsh > "${fpath[1]}/_ecg" | ||
# You will need to start a new shell for this setup to take effect. | ||
Fish: | ||
$ ecg completion fish | source | ||
# To load completions for each session, execute once: | ||
$ ecg completion fish > ~/.config/fish/completions/ecg.fish | ||
`, | ||
DisableFlagsInUseLine: true, | ||
ValidArgs: []string{"bash", "zsh", "fish"}, | ||
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
var err error | ||
switch args[0] { | ||
case "bash": | ||
err = cmd.Root().GenBashCompletion(os.Stdout) | ||
case "zsh": | ||
err = cmd.Root().GenZshCompletion(os.Stdout) | ||
case "fish": | ||
err = cmd.Root().GenFishCompletion(os.Stdout, true) | ||
} | ||
if err != nil { | ||
panic(err) | ||
} | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(completionCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"runtime/pprof" | ||
|
||
"github.com/anchore/elastic-container-gatherer/ecg/mode" | ||
|
||
"github.com/anchore/elastic-container-gatherer/ecg" | ||
"github.com/anchore/elastic-container-gatherer/ecg/presenter" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
) | ||
|
||
// rootCmd represents the base command when called without any subcommands | ||
var rootCmd = &cobra.Command{ | ||
Use: "ecg", | ||
Short: "ECG tells Anchore which images are in use in your ECS clusters", | ||
Long: "ECG (Elastic Container Gatherer) can poll Amazon ECS (Elastic Container Service) APIs to tell Anchore which Images are currently in-use", | ||
Args: cobra.MaximumNArgs(0), | ||
Run: func(cmd *cobra.Command, args []string) { | ||
if appConfig.Dev.ProfileCPU { | ||
f, err := os.Create("cpu.profile") | ||
if err != nil { | ||
log.Errorf("unable to create CPU profile: %+v", err) | ||
} else { | ||
err := pprof.StartCPUProfile(f) | ||
if err != nil { | ||
log.Errorf("unable to start CPU profile: %+v", err) | ||
} | ||
} | ||
} | ||
|
||
if len(args) > 0 { | ||
err := cmd.Help() | ||
if err != nil { | ||
log.Errorf(err.Error()) | ||
os.Exit(1) | ||
} | ||
os.Exit(1) | ||
} | ||
|
||
// TODO(bradjones) Validate anchore connection details here | ||
//if appConfig.AnchoreDetails.IsValid() { | ||
//dummyReport := inventory.Report{ | ||
//Results: []inventory.ReportItem{}, | ||
//} | ||
//err := reporter.Post(dummyReport, appConfig.AnchoreDetails, appConfig) | ||
//if err != nil { | ||
//log.Errorf("Failed to validate connection to Anchore: %+v", err) | ||
//} | ||
//} else { | ||
//log.Debug("Anchore details not specified, will not report inventory") | ||
//} | ||
|
||
switch appConfig.RunMode { | ||
case mode.PeriodicPolling: | ||
ecg.PeriodicallyGetInventoryReport(appConfig) | ||
default: | ||
report, err := ecg.GetInventoryReport(appConfig) | ||
if appConfig.Dev.ProfileCPU { | ||
pprof.StopCPUProfile() | ||
} | ||
if err != nil { | ||
log.Errorf("Failed to get Image Results: %+v", err) | ||
os.Exit(1) | ||
} else { | ||
err := ecg.HandleReport(report, appConfig) | ||
if err != nil { | ||
log.Errorf("Failed to handle Image Results: %+v", err) | ||
os.Exit(1) | ||
} | ||
} | ||
} | ||
}, | ||
} | ||
|
||
func init() { | ||
// output & formatting options | ||
opt := "output" | ||
rootCmd.Flags().StringP( | ||
opt, "o", presenter.JSONPresenter.String(), | ||
fmt.Sprintf("report output formatter, options=%v", presenter.Options), | ||
) | ||
if err := viper.BindPFlag(opt, rootCmd.Flags().Lookup(opt)); err != nil { | ||
fmt.Printf("unable to bind flag '%s': %+v", opt, err) | ||
os.Exit(1) | ||
} | ||
|
||
opt = "mode" | ||
rootCmd.Flags().StringP(opt, "m", mode.AdHoc.String(), fmt.Sprintf("execution mode, options=%v", mode.Modes)) | ||
if err := viper.BindPFlag(opt, rootCmd.Flags().Lookup(opt)); err != nil { | ||
fmt.Printf("unable to bind flag '%s': %+v", opt, err) | ||
os.Exit(1) | ||
} | ||
|
||
opt = "polling-interval-seconds" | ||
rootCmd.Flags().StringP(opt, "p", "300", "If mode is 'periodic', this specifies the interval") | ||
if err := viper.BindPFlag(opt, rootCmd.Flags().Lookup(opt)); err != nil { | ||
fmt.Printf("unable to bind flag '%s': %+v", opt, err) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package cmd | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/anchore/elastic-container-gatherer/internal" | ||
"github.com/anchore/elastic-container-gatherer/internal/version" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var outputFormat string | ||
|
||
var versionCmd = &cobra.Command{ | ||
Use: "version", | ||
Short: "show the version", | ||
Run: printVersion, | ||
} | ||
|
||
func init() { | ||
versionCmd.Flags().StringVarP(&outputFormat, "output", "o", "text", "format to show version information (available=[text, json])") | ||
|
||
rootCmd.AddCommand(versionCmd) | ||
} | ||
|
||
func printVersion(_ *cobra.Command, _ []string) { | ||
versionInfo := version.FromBuild() | ||
switch outputFormat { | ||
case "text": | ||
fmt.Println("Application: ", internal.ApplicationName) | ||
fmt.Println("Version: ", versionInfo.Version) | ||
fmt.Println("BuildDate: ", versionInfo.BuildDate) | ||
fmt.Println("GitCommit: ", versionInfo.GitCommit) | ||
fmt.Println("GitTreeState: ", versionInfo.GitTreeState) | ||
fmt.Println("Platform: ", versionInfo.Platform) | ||
fmt.Println("GoVersion: ", versionInfo.GoVersion) | ||
fmt.Println("Compiler: ", versionInfo.Compiler) | ||
case "json": | ||
|
||
enc := json.NewEncoder(os.Stdout) | ||
enc.SetEscapeHTML(false) | ||
enc.SetIndent("", " ") | ||
err := enc.Encode(&struct { | ||
version.Version | ||
Application string `json:"application"` | ||
}{ | ||
Version: versionInfo, | ||
Application: internal.ApplicationName, | ||
}) | ||
if err != nil { | ||
fmt.Printf("failed to show version information: %+v\n", err) | ||
os.Exit(1) | ||
} | ||
default: | ||
fmt.Printf("unsupported output format: %s\n", outputFormat) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package inventory | ||
|
||
type Report struct { | ||
Timestamp string `json:"timestamp,omitempty"` // Should be generated using time.Now.UTC() and formatted according to RFC Y-M-DTH:M:SZ | ||
Results []ReportItem `json:"results"` | ||
ClusterName string `json:"cluster_name"` | ||
InventoryType string `json:"inventory_type"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package inventory | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// ReportItem represents a cluster and all it's unique images | ||
type ReportItem struct { | ||
Cluster string `json:"cluster,omitempty"` | ||
Images []ReportImage `json:"images"` | ||
} | ||
|
||
// ReportImage represents a unique image in a cluster | ||
type ReportImage struct { | ||
Tag string `json:"tag,omitempty"` | ||
RepoDigest string `json:"repoDigest,omitempty"` | ||
} | ||
|
||
// String represent the ReportItem as a string | ||
func (r *ReportItem) String() string { | ||
return fmt.Sprintf("ReportItem(cluster=%s, images=%v)", r.Cluster, r.Images) | ||
} | ||
|
||
// key will return a unique key for a ReportImage | ||
func (i *ReportImage) key() string { | ||
return fmt.Sprintf("%s@%s", i.Tag, i.RepoDigest) | ||
} |
Oops, something went wrong.