From fae4fdeabe0cf4d4aa6e6814a02538de7efa2d33 Mon Sep 17 00:00:00 2001 From: koplas Date: Tue, 18 Jun 2024 14:16:00 +0200 Subject: [PATCH] Move downloader to lib/downloader --- cmd/csaf_downloader/main.go | 82 +++++++++++-- .../downloader}/config.go | 111 +++++------------- .../downloader}/downloader.go | 69 +++++------ .../downloader}/forwarder.go | 52 ++++---- .../downloader}/forwarder_test.go | 38 +++--- .../downloader}/stats.go | 2 +- .../downloader}/stats_test.go | 2 +- 7 files changed, 182 insertions(+), 174 deletions(-) rename {cmd/csaf_downloader => lib/downloader}/config.go (73%) rename {cmd/csaf_downloader => lib/downloader}/downloader.go (91%) rename {cmd/csaf_downloader => lib/downloader}/forwarder.go (85%) rename {cmd/csaf_downloader => lib/downloader}/forwarder_test.go (95%) rename {cmd/csaf_downloader => lib/downloader}/stats.go (98%) rename {cmd/csaf_downloader => lib/downloader}/stats_test.go (99%) diff --git a/cmd/csaf_downloader/main.go b/cmd/csaf_downloader/main.go index cc284bb..295b0a3 100644 --- a/cmd/csaf_downloader/main.go +++ b/cmd/csaf_downloader/main.go @@ -11,6 +11,7 @@ package main import ( "context" + "github.com/csaf-poc/csaf_distribution/v3/lib/downloader" "log/slog" "os" "os/signal" @@ -18,12 +19,71 @@ import ( "github.com/csaf-poc/csaf_distribution/v3/internal/options" ) -func run(cfg *config, domains []string) error { - d, err := newDownloader(cfg) +const ( + defaultWorker = 2 + defaultPreset = "mandatory" + defaultForwardQueue = 5 + defaultValidationMode = downloader.ValidationStrict + defaultLogFile = "downloader.log" + defaultLogLevel = slog.LevelInfo +) + +// configPaths are the potential file locations of the Config file. +var configPaths = []string{ + "~/.config/csaf/downloader.toml", + "~/.csaf_downloader.toml", + "csaf_downloader.toml", +} + +// parseArgsConfig parses the command line and if needed a config file. +func parseArgsConfig() ([]string, *downloader.Config, error) { + var ( + logFile = defaultLogFile + logLevel = &options.LogLevel{Level: defaultLogLevel} + ) + p := options.Parser[downloader.Config]{ + DefaultConfigLocations: configPaths, + ConfigLocation: func(cfg *downloader.Config) string { return cfg.Config }, + Usage: "[OPTIONS] domain...", + HasVersion: func(cfg *downloader.Config) bool { return cfg.Version }, + SetDefaults: func(cfg *downloader.Config) { + cfg.Worker = defaultWorker + cfg.RemoteValidatorPresets = []string{defaultPreset} + cfg.ValidationMode = defaultValidationMode + cfg.ForwardQueue = defaultForwardQueue + cfg.LogFile = &logFile + cfg.LogLevel = logLevel + }, + // Re-establish default values if not set. + EnsureDefaults: func(cfg *downloader.Config) { + if cfg.Worker == 0 { + cfg.Worker = defaultWorker + } + if cfg.RemoteValidatorPresets == nil { + cfg.RemoteValidatorPresets = []string{defaultPreset} + } + switch cfg.ValidationMode { + case downloader.ValidationStrict, downloader.ValidationUnsafe: + default: + cfg.ValidationMode = downloader.ValidationStrict + } + if cfg.LogFile == nil { + cfg.LogFile = &logFile + } + if cfg.LogLevel == nil { + cfg.LogLevel = logLevel + } + }, + } + return p.Parse() +} + +func run(cfg *downloader.Config, domains []string) error { + d, err := downloader.NewDownloader(cfg) if err != nil { return err } - defer d.close() + defer d.Close() ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -31,28 +91,28 @@ func run(cfg *config, domains []string) error { defer stop() if cfg.ForwardURL != "" { - f := newForwarder(cfg) - go f.run() + f := downloader.NewForwarder(cfg) + go f.Run() defer func() { - f.log() - f.close() + f.Log() + f.Close() }() - d.forwarder = f + d.Forwarder = f } // If the enumerate-only flag is set, enumerate found PMDs, // else use the normal load method if cfg.EnumeratePMDOnly { - return d.runEnumerate(domains) + return d.RunEnumerate(domains) } - return d.run(ctx, domains) + return d.Run(ctx, domains) } func main() { domains, cfg, err := parseArgsConfig() options.ErrorCheck(err) - options.ErrorCheck(cfg.prepare()) + options.ErrorCheck(cfg.Prepare()) if len(domains) == 0 { slog.Warn("No domains given.") diff --git a/cmd/csaf_downloader/config.go b/lib/downloader/config.go similarity index 73% rename from cmd/csaf_downloader/config.go rename to lib/downloader/config.go index dcfc090..30121ca 100644 --- a/cmd/csaf_downloader/config.go +++ b/lib/downloader/config.go @@ -6,7 +6,7 @@ // SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) // Software-Engineering: 2022 Intevation GmbH -package main +package downloader import ( "crypto/tls" @@ -25,23 +25,18 @@ import ( "github.com/csaf-poc/csaf_distribution/v3/internal/options" ) -const ( - defaultWorker = 2 - defaultPreset = "mandatory" - defaultForwardQueue = 5 - defaultValidationMode = validationStrict - defaultLogFile = "downloader.log" - defaultLogLevel = slog.LevelInfo -) - -type validationMode string +// ValidationMode specifies the strict the validation is. +type ValidationMode string const ( - validationStrict = validationMode("strict") - validationUnsafe = validationMode("unsafe") + // ValidationStrict skips advisories with failed validation. + ValidationStrict = ValidationMode("strict") + // ValidationUnsafe allows advisories with failed validation. + ValidationUnsafe = ValidationMode("unsafe") ) -type config struct { +// Config provides the download configuration. +type Config struct { Directory string `short:"d" long:"directory" description:"DIRectory to store the downloaded files in" value-name:"DIR" toml:"directory"` Insecure bool `long:"insecure" description:"Do not check TLS certificates from provider" toml:"insecure"` IgnoreSignatureCheck bool `long:"ignore_sigcheck" description:"Ignore signature check results, just warn on mismatch" toml:"ignore_sigcheck"` @@ -64,7 +59,7 @@ type config struct { RemoteValidatorPresets []string `long:"validator_preset" description:"One or more PRESETS to validate remotely" value-name:"PRESETS" toml:"validator_preset"` //lint:ignore SA5008 We are using choice twice: strict, unsafe. - ValidationMode validationMode `long:"validation_mode" short:"m" choice:"strict" choice:"unsafe" value-name:"MODE" description:"MODE how strict the validation is" toml:"validation_mode"` + ValidationMode ValidationMode `long:"validation_mode" short:"m" choice:"strict" choice:"unsafe" value-name:"MODE" description:"MODE how strict the validation is" toml:"validation_mode"` ForwardURL string `long:"forward_url" description:"URL of HTTP endpoint to forward downloads to" value-name:"URL" toml:"forward_url"` ForwardHeader http.Header `long:"forward_header" description:"One or more extra HTTP header fields used by forwarding" toml:"forward_header"` @@ -81,60 +76,10 @@ type config struct { ignorePattern filter.PatternMatcher } -// configPaths are the potential file locations of the config file. -var configPaths = []string{ - "~/.config/csaf/downloader.toml", - "~/.csaf_downloader.toml", - "csaf_downloader.toml", -} - -// parseArgsConfig parses the command line and if need a config file. -func parseArgsConfig() ([]string, *config, error) { - var ( - logFile = defaultLogFile - logLevel = &options.LogLevel{Level: defaultLogLevel} - ) - p := options.Parser[config]{ - DefaultConfigLocations: configPaths, - ConfigLocation: func(cfg *config) string { return cfg.Config }, - Usage: "[OPTIONS] domain...", - HasVersion: func(cfg *config) bool { return cfg.Version }, - SetDefaults: func(cfg *config) { - cfg.Worker = defaultWorker - cfg.RemoteValidatorPresets = []string{defaultPreset} - cfg.ValidationMode = defaultValidationMode - cfg.ForwardQueue = defaultForwardQueue - cfg.LogFile = &logFile - cfg.LogLevel = logLevel - }, - // Re-establish default values if not set. - EnsureDefaults: func(cfg *config) { - if cfg.Worker == 0 { - cfg.Worker = defaultWorker - } - if cfg.RemoteValidatorPresets == nil { - cfg.RemoteValidatorPresets = []string{defaultPreset} - } - switch cfg.ValidationMode { - case validationStrict, validationUnsafe: - default: - cfg.ValidationMode = validationStrict - } - if cfg.LogFile == nil { - cfg.LogFile = &logFile - } - if cfg.LogLevel == nil { - cfg.LogLevel = logLevel - } - }, - } - return p.Parse() -} - // UnmarshalText implements [encoding.TextUnmarshaler]. -func (vm *validationMode) UnmarshalText(text []byte) error { - switch m := validationMode(text); m { - case validationStrict, validationUnsafe: +func (vm *ValidationMode) UnmarshalText(text []byte) error { + switch m := ValidationMode(text); m { + case ValidationStrict, ValidationUnsafe: *vm = m default: return fmt.Errorf(`invalid value %q (expected "strict" or "unsafe)"`, m) @@ -143,8 +88,8 @@ func (vm *validationMode) UnmarshalText(text []byte) error { } // UnmarshalFlag implements [flags.UnmarshalFlag]. -func (vm *validationMode) UnmarshalFlag(value string) error { - var v validationMode +func (vm *ValidationMode) UnmarshalFlag(value string) error { + var v ValidationMode if err := v.UnmarshalText([]byte(value)); err != nil { return err } @@ -153,18 +98,18 @@ func (vm *validationMode) UnmarshalFlag(value string) error { } // ignoreFile returns true if the given URL should not be downloaded. -func (cfg *config) ignoreURL(u string) bool { +func (cfg *Config) ignoreURL(u string) bool { return cfg.ignorePattern.Matches(u) } // verbose is considered a log level equal or less debug. -func (cfg *config) verbose() bool { +func (cfg *Config) verbose() bool { return cfg.LogLevel.Level <= slog.LevelDebug } // prepareDirectory ensures that the working directory // exists and is setup properly. -func (cfg *config) prepareDirectory() error { +func (cfg *Config) prepareDirectory() error { // If not given use current working directory. if cfg.Directory == "" { dir, err := os.Getwd() @@ -198,7 +143,7 @@ func dropSubSeconds(_ []string, a slog.Attr) slog.Attr { } // prepareLogging sets up the structured logging. -func (cfg *config) prepareLogging() error { +func (cfg *Config) prepareLogging() error { var w io.Writer if cfg.LogFile == nil || *cfg.LogFile == "" { log.Println("using STDERR for logging") @@ -231,7 +176,7 @@ func (cfg *config) prepareLogging() error { } // compileIgnorePatterns compiles the configure patterns to be ignored. -func (cfg *config) compileIgnorePatterns() error { +func (cfg *Config) compileIgnorePatterns() error { pm, err := filter.NewPatternMatcher(cfg.IgnorePattern) if err != nil { return err @@ -241,7 +186,7 @@ func (cfg *config) compileIgnorePatterns() error { } // prepareCertificates loads the client side certificates used by the HTTP client. -func (cfg *config) prepareCertificates() error { +func (cfg *Config) prepareCertificates() error { cert, err := certs.LoadCertificate( cfg.ClientCert, cfg.ClientKey, cfg.ClientPassphrase) if err != nil { @@ -251,13 +196,13 @@ func (cfg *config) prepareCertificates() error { return nil } -// prepare prepares internal state of a loaded configuration. -func (cfg *config) prepare() error { - for _, prepare := range []func(*config) error{ - (*config).prepareDirectory, - (*config).prepareLogging, - (*config).prepareCertificates, - (*config).compileIgnorePatterns, +// Prepare prepares internal state of a loaded configuration. +func (cfg *Config) Prepare() error { + for _, prepare := range []func(*Config) error{ + (*Config).prepareDirectory, + (*Config).prepareLogging, + (*Config).prepareCertificates, + (*Config).compileIgnorePatterns, } { if err := prepare(cfg); err != nil { return err diff --git a/cmd/csaf_downloader/downloader.go b/lib/downloader/downloader.go similarity index 91% rename from cmd/csaf_downloader/downloader.go rename to lib/downloader/downloader.go index badf060..5f71994 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/lib/downloader/downloader.go @@ -6,7 +6,7 @@ // SPDX-FileCopyrightText: 2022, 2023 German Federal Office for Information Security (BSI) // Software-Engineering: 2022, 2023 Intevation GmbH -package main +package downloader import ( "bytes" @@ -37,11 +37,12 @@ import ( "github.com/csaf-poc/csaf_distribution/v3/util" ) -type downloader struct { - cfg *config +// Downloader provides the CSAF downloader. +type Downloader struct { + cfg *Config keys *crypto.KeyRing validator csaf.RemoteValidator - forwarder *forwarder + Forwarder *Forwarder mkdirMu sync.Mutex statsMu sync.Mutex stats stats @@ -52,7 +53,8 @@ type downloader struct { // unsafe mode. const failedValidationDir = "failed_validation" -func newDownloader(cfg *config) (*downloader, error) { +// NewDownloader constructs a new downloader given the configuration. +func NewDownloader(cfg *Config) (*Downloader, error) { var validator csaf.RemoteValidator @@ -70,13 +72,14 @@ func newDownloader(cfg *config) (*downloader, error) { validator = csaf.SynchronizedRemoteValidator(validator) } - return &downloader{ + return &Downloader{ cfg: cfg, validator: validator, }, nil } -func (d *downloader) close() { +// Close closes the downloader. +func (d *Downloader) Close() { if d.validator != nil { d.validator.Close() d.validator = nil @@ -84,7 +87,7 @@ func (d *downloader) close() { } // addStats add stats to total stats -func (d *downloader) addStats(o *stats) { +func (d *Downloader) addStats(o *stats) { d.statsMu.Lock() defer d.statsMu.Unlock() d.stats.add(o) @@ -102,7 +105,7 @@ func logRedirect(req *http.Request, via []*http.Request) error { return nil } -func (d *downloader) httpClient() util.Client { +func (d *Downloader) httpClient() util.Client { hClient := http.Client{} @@ -162,7 +165,7 @@ func httpLog(who string) func(string, string) { } } -func (d *downloader) enumerate(domain string) error { +func (d *Downloader) enumerate(domain string) error { client := d.httpClient() loader := csaf.NewProviderMetadataLoader(client) @@ -192,7 +195,7 @@ func (d *downloader) enumerate(domain string) error { return nil } -func (d *downloader) download(ctx context.Context, domain string) error { +func (d *Downloader) download(ctx context.Context, domain string) error { client := d.httpClient() loader := csaf.NewProviderMetadataLoader(client) @@ -248,7 +251,7 @@ func (d *downloader) download(ctx context.Context, domain string) error { }) } -func (d *downloader) downloadFiles( +func (d *Downloader) downloadFiles( ctx context.Context, label csaf.TLPLabel, files []csaf.AdvisoryFile, @@ -297,7 +300,7 @@ allFiles: return errors.Join(errs...) } -func (d *downloader) loadOpenPGPKeys( +func (d *Downloader) loadOpenPGPKeys( client util.Client, doc any, base *url.URL, @@ -389,7 +392,7 @@ func (d *downloader) loadOpenPGPKeys( } // logValidationIssues logs the issues reported by the advisory schema validation. -func (d *downloader) logValidationIssues(url string, errors []string, err error) { +func (d *Downloader) logValidationIssues(url string, errors []string, err error) { if err != nil { slog.Error("Failed to validate", "url", url, @@ -409,7 +412,7 @@ func (d *downloader) logValidationIssues(url string, errors []string, err error) } } -func (d *downloader) downloadWorker( +func (d *Downloader) downloadWorker( ctx context.Context, wg *sync.WaitGroup, label csaf.TLPLabel, @@ -584,9 +587,9 @@ nextAdvisory: // Validate against CSAF schema. schemaCheck := func() error { - if errors, err := csaf.ValidateCSAF(doc); err != nil || len(errors) > 0 { + if errs, err := csaf.ValidateCSAF(doc); err != nil || len(errs) > 0 { stats.schemaFailed++ - d.logValidationIssues(file.URL(), errors, err) + d.logValidationIssues(file.URL(), errs, err) return fmt.Errorf("schema validation for %q failed", file.URL()) } return nil @@ -633,16 +636,16 @@ nextAdvisory: if err := check(); err != nil { slog.Error("Validation check failed", "error", err) valStatus.update(invalidValidationStatus) - if d.cfg.ValidationMode == validationStrict { + if d.cfg.ValidationMode == ValidationStrict { continue nextAdvisory } } } valStatus.update(validValidationStatus) - // Send to forwarder - if d.forwarder != nil { - d.forwarder.forward( + // Send to Forwarder + if d.Forwarder != nil { + d.Forwarder.forward( filename, data.String(), valStatus, string(s256Data), @@ -690,17 +693,17 @@ nextAdvisory: } // Write advisory to file - path := filepath.Join(lastDir, filename) + filePath := filepath.Join(lastDir, filename) // Write data to disk. for _, x := range []struct { p string d []byte }{ - {path, data.Bytes()}, - {path + ".sha256", s256Data}, - {path + ".sha512", s512Data}, - {path + ".asc", signData}, + {filePath, data.Bytes()}, + {filePath + ".sha256", s256Data}, + {filePath + ".sha512", s512Data}, + {filePath + ".asc", signData}, } { if x.d != nil { if err := os.WriteFile(x.p, x.d, 0644); err != nil { @@ -711,17 +714,17 @@ nextAdvisory: } stats.succeeded++ - slog.Info("Written advisory", "path", path) + slog.Info("Written advisory", "path", filePath) } } -func (d *downloader) mkdirAll(path string, perm os.FileMode) error { +func (d *Downloader) mkdirAll(path string, perm os.FileMode) error { d.mkdirMu.Lock() defer d.mkdirMu.Unlock() return os.MkdirAll(path, perm) } -func (d *downloader) checkSignature(data []byte, sign *crypto.PGPSignature) error { +func (d *Downloader) checkSignature(data []byte, sign *crypto.PGPSignature) error { pm := crypto.NewPlainMessage(data) t := crypto.GetUnixTime() return d.keys.VerifyDetached(pm, sign, t) @@ -767,8 +770,8 @@ func loadHash(client util.Client, p string) ([]byte, []byte, error) { return hash, data.Bytes(), nil } -// run performs the downloads for all the given domains. -func (d *downloader) run(ctx context.Context, domains []string) error { +// Run performs the downloads for all the given domains. +func (d *Downloader) Run(ctx context.Context, domains []string) error { defer d.stats.log() for _, domain := range domains { if err := d.download(ctx, domain); err != nil { @@ -778,8 +781,8 @@ func (d *downloader) run(ctx context.Context, domains []string) error { return nil } -// runEnumerate performs the enumeration of PMDs for all the given domains. -func (d *downloader) runEnumerate(domains []string) error { +// RunEnumerate performs the enumeration of PMDs for all the given domains. +func (d *Downloader) RunEnumerate(domains []string) error { defer d.stats.log() for _, domain := range domains { if err := d.enumerate(domain); err != nil { diff --git a/cmd/csaf_downloader/forwarder.go b/lib/downloader/forwarder.go similarity index 85% rename from cmd/csaf_downloader/forwarder.go rename to lib/downloader/forwarder.go index 3b1435a..78adcae 100644 --- a/cmd/csaf_downloader/forwarder.go +++ b/lib/downloader/forwarder.go @@ -6,7 +6,7 @@ // SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) // Software-Engineering: 2023 Intevation GmbH -package main +package downloader import ( "bytes" @@ -44,46 +44,46 @@ func (vs *validationStatus) update(status validationStatus) { } } -// forwarder forwards downloaded advisories to a given +// Forwarder forwards downloaded advisories to a given // HTTP endpoint. -type forwarder struct { - cfg *config - cmds chan func(*forwarder) +type Forwarder struct { + cfg *Config + cmds chan func(*Forwarder) client util.Client failed int succeeded int } -// newForwarder creates a new forwarder. -func newForwarder(cfg *config) *forwarder { +// NewForwarder creates a new Forwarder. +func NewForwarder(cfg *Config) *Forwarder { queue := cfg.ForwardQueue if queue < 1 { queue = 1 } - return &forwarder{ + return &Forwarder{ cfg: cfg, - cmds: make(chan func(*forwarder), queue), + cmds: make(chan func(*Forwarder), queue), } } -// run runs the forwarder. Meant to be used in a Go routine. -func (f *forwarder) run() { - defer slog.Debug("forwarder done") +// Run runs the Forwarder. Meant to be used in a Go routine. +func (f *Forwarder) Run() { + defer slog.Debug("Forwarder done") for cmd := range f.cmds { cmd(f) } } -// close terminates the forwarder. -func (f *forwarder) close() { +// Close terminates the Forwarder. +func (f *Forwarder) Close() { close(f.cmds) } -// log logs the current statistics. -func (f *forwarder) log() { - f.cmds <- func(f *forwarder) { +// Log logs the current statistics. +func (f *Forwarder) Log() { + f.cmds <- func(f *Forwarder) { slog.Info("Forward statistics", "succeeded", f.succeeded, "failed", f.failed) @@ -92,7 +92,7 @@ func (f *forwarder) log() { // httpClient returns a cached HTTP client used for uploading // the advisories to the configured HTTP endpoint. -func (f *forwarder) httpClient() util.Client { +func (f *Forwarder) httpClient() util.Client { if f.client != nil { return f.client } @@ -122,7 +122,7 @@ func (f *forwarder) httpClient() util.Client { if f.cfg.verbose() { client = &util.LoggingClient{ Client: client, - Log: httpLog("forwarder"), + Log: httpLog("Forwarder"), } } @@ -137,7 +137,7 @@ func replaceExt(fname, nExt string) string { } // buildRequest creates an HTTP request suited to forward the given advisory. -func (f *forwarder) buildRequest( +func (f *Forwarder) buildRequest( filename, doc string, status validationStatus, sha256, sha512 string, @@ -189,7 +189,7 @@ func (f *forwarder) buildRequest( // storeFailedAdvisory stores an advisory in a special folder // in case the forwarding failed. -func (f *forwarder) storeFailedAdvisory(filename, doc, sha256, sha512 string) error { +func (f *Forwarder) storeFailedAdvisory(filename, doc, sha256, sha512 string) error { // Create special folder if it does not exist. dir := filepath.Join(f.cfg.Directory, failedForwardDir) if err := os.MkdirAll(dir, 0755); err != nil { @@ -215,7 +215,7 @@ func (f *forwarder) storeFailedAdvisory(filename, doc, sha256, sha512 string) er } // storeFailed is a logging wrapper around storeFailedAdvisory. -func (f *forwarder) storeFailed(filename, doc, sha256, sha512 string) { +func (f *Forwarder) storeFailed(filename, doc, sha256, sha512 string) { f.failed++ if err := f.storeFailedAdvisory(filename, doc, sha256, sha512); err != nil { slog.Error("Storing advisory failed forwarding failed", @@ -237,15 +237,15 @@ func limitedString(r io.Reader, max int) (string, error) { } // forward sends a given document with filename, status and -// checksums to the forwarder. This is async to the degree +// checksums to the Forwarder. This is async to the degree // till the configured queue size is filled. -func (f *forwarder) forward( +func (f *Forwarder) forward( filename, doc string, status validationStatus, sha256, sha512 string, ) { - // Run this in the main loop of the forwarder. - f.cmds <- func(f *forwarder) { + // Run this in the main loop of the Forwarder. + f.cmds <- func(f *Forwarder) { req, err := f.buildRequest(filename, doc, status, sha256, sha512) if err != nil { slog.Error("building forward Request failed", diff --git a/cmd/csaf_downloader/forwarder_test.go b/lib/downloader/forwarder_test.go similarity index 95% rename from cmd/csaf_downloader/forwarder_test.go rename to lib/downloader/forwarder_test.go index 907bbce..8339d10 100644 --- a/cmd/csaf_downloader/forwarder_test.go +++ b/lib/downloader/forwarder_test.go @@ -6,7 +6,7 @@ // SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) // Software-Engineering: 2023 Intevation GmbH -package main +package downloader import ( "bufio" @@ -53,18 +53,18 @@ func TestForwarderLogStats(t *testing.T) { lg := slog.New(h) slog.SetDefault(lg) - cfg := &config{} - fw := newForwarder(cfg) + cfg := &Config{} + fw := NewForwarder(cfg) fw.failed = 11 fw.succeeded = 13 done := make(chan struct{}) go func() { defer close(done) - fw.run() + fw.Run() }() - fw.log() - fw.close() + fw.Log() + fw.Close() <-done type fwStats struct { @@ -95,14 +95,14 @@ func TestForwarderLogStats(t *testing.T) { } func TestForwarderHTTPClient(t *testing.T) { - cfg := &config{ + cfg := &Config{ ForwardInsecure: true, ForwardHeader: http.Header{ "User-Agent": []string{"curl/7.55.1"}, }, LogLevel: &options.LogLevel{Level: slog.LevelDebug}, } - fw := newForwarder(cfg) + fw := NewForwarder(cfg) if c1, c2 := fw.httpClient(), fw.httpClient(); c1 != c2 { t.Fatal("expected to return same client twice") } @@ -124,10 +124,10 @@ func TestForwarderReplaceExtension(t *testing.T) { func TestForwarderBuildRequest(t *testing.T) { // Good case ... - cfg := &config{ + cfg := &Config{ ForwardURL: "https://example.com", } - fw := newForwarder(cfg) + fw := NewForwarder(cfg) req, err := fw.buildRequest( "test.json", "{}", @@ -248,8 +248,8 @@ func TestStoreFailedAdvisory(t *testing.T) { } defer os.RemoveAll(dir) - cfg := &config{Directory: dir} - fw := newForwarder(cfg) + cfg := &Config{Directory: dir} + fw := NewForwarder(cfg) badDir := filepath.Join(dir, failedForwardDir) if err := os.WriteFile(badDir, []byte("test"), 0664); err != nil { @@ -301,8 +301,8 @@ func TestStoredFailed(t *testing.T) { lg := slog.New(h) slog.SetDefault(lg) - cfg := &config{Directory: dir} - fw := newForwarder(cfg) + cfg := &Config{Directory: dir} + fw := NewForwarder(cfg) // An empty filename should lead to an error. fw.storeFailed("", "{}", "256", "512") @@ -385,11 +385,11 @@ func TestForwarderForward(t *testing.T) { lg := slog.New(h) slog.SetDefault(lg) - cfg := &config{ + cfg := &Config{ ForwardURL: "http://example.com", Directory: dir, } - fw := newForwarder(cfg) + fw := NewForwarder(cfg) // Use the fact that http client is cached. fw.client = &fakeClient{} @@ -398,7 +398,7 @@ func TestForwarderForward(t *testing.T) { go func() { defer close(done) - fw.run() + fw.Run() }() // Iterate through states of http client. @@ -412,7 +412,7 @@ func TestForwarderForward(t *testing.T) { // Make buildRequest fail. wait := make(chan struct{}) - fw.cmds <- func(f *forwarder) { + fw.cmds <- func(f *Forwarder) { f.cfg.ForwardURL = "%" close(wait) } @@ -423,7 +423,7 @@ func TestForwarderForward(t *testing.T) { "256", "512") - fw.close() + fw.Close() <-done } diff --git a/cmd/csaf_downloader/stats.go b/lib/downloader/stats.go similarity index 98% rename from cmd/csaf_downloader/stats.go rename to lib/downloader/stats.go index d85feb3..944f6b5 100644 --- a/cmd/csaf_downloader/stats.go +++ b/lib/downloader/stats.go @@ -6,7 +6,7 @@ // SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) // Software-Engineering: 2023 Intevation GmbH -package main +package downloader import "log/slog" diff --git a/cmd/csaf_downloader/stats_test.go b/lib/downloader/stats_test.go similarity index 99% rename from cmd/csaf_downloader/stats_test.go rename to lib/downloader/stats_test.go index 033fd7b..9d3a0f2 100644 --- a/cmd/csaf_downloader/stats_test.go +++ b/lib/downloader/stats_test.go @@ -6,7 +6,7 @@ // SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) // Software-Engineering: 2023 Intevation GmbH -package main +package downloader import ( "bytes"