diff --git a/cmd/csaf_aggregator/config.go b/cmd/csaf_aggregator/config.go index 3943a44..a389704 100644 --- a/cmd/csaf_aggregator/config.go +++ b/cmd/csaf_aggregator/config.go @@ -26,11 +26,12 @@ import ( ) const ( - defaultConfigPath = "aggregator.toml" - defaultWorkers = 10 - defaultFolder = "/var/www" - defaultWeb = "/var/www/html" - defaultDomain = "https://example.com" + defaultConfigPath = "aggregator.toml" + defaultWorkers = 10 + defaultFolder = "/var/www" + defaultWeb = "/var/www/html" + defaultDomain = "https://example.com" + defaultUpdateInterval = "on best effort" ) type provider struct { @@ -44,6 +45,9 @@ type provider struct { // ServiceDocument incidates if we should create a service.json document. ServiceDocument *bool `toml:"create_service_document"` AggregatoryCategory *csaf.AggregatorCategory `toml:"category"` + + // UpdateInterval is as the mandatory `update_interval` if this is a publisher. + UpdateInterval *string `toml:"update_interval"` } type config struct { @@ -81,6 +85,10 @@ type config struct { // ServiceDocument incidates if we should create a service.json document. ServiceDocument bool `toml:"create_service_document"` + // UpdateInterval is used for publishers a the mandatory field + // 'update_interval'. + UpdateInterval *string + keyMu sync.Mutex key *crypto.Key keyErr error @@ -96,6 +104,17 @@ func (c *config) tooOldForInterims() func(time.Time) bool { return func(t time.Time) bool { return t.Before(from) } } +// updateInterval returns the update interval of a publisher. +func (p *provider) updateInterval(c *config) string { + if p.UpdateInterval != nil { + return *p.UpdateInterval + } + if c.UpdateInterval != nil { + return *c.UpdateInterval + } + return defaultUpdateInterval +} + // serviceDocument tells if we should generate a service document for a // given provider. func (p *provider) serviceDocument(c *config) bool { diff --git a/cmd/csaf_aggregator/full.go b/cmd/csaf_aggregator/full.go index fef169c..b9b3005 100644 --- a/cmd/csaf_aggregator/full.go +++ b/cmd/csaf_aggregator/full.go @@ -14,6 +14,7 @@ import ( "log" "os" "path/filepath" + "strings" "sync" "time" @@ -128,8 +129,8 @@ func (p *processor) full() error { wg.Wait() // Assemble aggregator data structure. - - csafProviders := make([]*csaf.AggregatorCSAFProvider, 0, len(jobs)) + var providers []*csaf.AggregatorCSAFProvider + var publishers []*csaf.AggregatorCSAFPublisher for i := range jobs { j := &jobs[i] @@ -142,10 +143,21 @@ func (p *processor) full() error { "error: '%s' does not produce any result.\n", j.provider.Name) continue } - csafProviders = append(csafProviders, j.aggregatorProvider) + + // "https://" signals a publisher. + if strings.HasPrefix(j.provider.Domain, "https://") { + pub := &csaf.AggregatorCSAFPublisher{ + Metadata: j.aggregatorProvider.Metadata, + Mirrors: j.aggregatorProvider.Mirrors, + UpdateInterval: j.provider.updateInterval(p.cfg), + } + publishers = append(publishers, pub) + } else { + providers = append(providers, j.aggregatorProvider) + } } - if len(csafProviders) == 0 { + if len(providers)+len(publishers) == 0 { return errors.New("all jobs failed, stopping") } @@ -156,11 +168,12 @@ func (p *processor) full() error { lastUpdated := csaf.TimeStamp(time.Now().UTC()) agg := csaf.Aggregator{ - Aggregator: &p.cfg.Aggregator, - Version: &version, - CanonicalURL: &canonicalURL, - CSAFProviders: csafProviders, - LastUpdated: &lastUpdated, + Aggregator: &p.cfg.Aggregator, + Version: &version, + CanonicalURL: &canonicalURL, + CSAFProviders: providers, + CSAFPublishers: publishers, + LastUpdated: &lastUpdated, } web := filepath.Join(p.cfg.Web, ".well-known", "csaf-aggregator") diff --git a/csaf/models.go b/csaf/models.go index 8935d41..efc2229 100644 --- a/csaf/models.go +++ b/csaf/models.go @@ -218,13 +218,21 @@ type AggregatorCSAFProvider struct { Mirrors []ProviderURL `json:"mirrors,omitempty"` // required } +// AggregatorCSAFPublisher reflects one publisher in an aggregator. +type AggregatorCSAFPublisher struct { + Metadata *AggregatorCSAFProviderMetadata `json:"metadata,omitempty"` // required + Mirrors []ProviderURL `json:"mirrors,omitempty"` // required + UpdateInterval string `json:"update_interval,omitempty"` // required +} + // Aggregator is the CSAF Aggregator. type Aggregator struct { - Aggregator *AggregatorInfo `json:"aggregator,omitempty"` // required - Version *AggregatorVersion `json:"aggregator_version,omitempty"` // required - CanonicalURL *AggregatorURL `json:"canonical_url,omitempty"` // required - CSAFProviders []*AggregatorCSAFProvider `json:"csaf_providers,omitempty"` // required - LastUpdated *TimeStamp `json:"last_updated,omitempty"` // required + Aggregator *AggregatorInfo `json:"aggregator,omitempty"` // required + Version *AggregatorVersion `json:"aggregator_version,omitempty"` // required + CanonicalURL *AggregatorURL `json:"canonical_url,omitempty"` // required + CSAFProviders []*AggregatorCSAFProvider `json:"csaf_providers,omitempty"` // required + CSAFPublishers []*AggregatorCSAFPublisher `json:"csaf_publishers,omitempty"` + LastUpdated *TimeStamp `json:"last_updated,omitempty"` // required } // Validate validates the current state of the AggregatorCategory.