diff --git a/cmd/krel/cmd/changelog_data_test.go b/cmd/krel/cmd/changelog_data_test.go
index 80c792c27c4..b004cca4f01 100644
--- a/cmd/krel/cmd/changelog_data_test.go
+++ b/cmd/krel/cmd/changelog_data_test.go
@@ -52,7 +52,6 @@ const patchReleaseExpectedContent = `## Changes by Kind
- Restores compatibility of kube-scheduler with clusters that do not enable the events.k8s.io/v1beta1 API ([#84465](https://github.com/kubernetes/kubernetes/pull/84465), [@yastij](https://github.com/yastij)) [SIG API Machinery and Scheduling]
- Switched intstr.Type to sized integer to follow API guidelines and improve compatibility with proto libraries ([#83956](https://github.com/kubernetes/kubernetes/pull/83956), [@liggitt](https://github.com/liggitt)) [SIG API Machinery]
- Update Cluster Autoscaler version to 1.16.2 (CA release docs: https://github.com/kubernetes/autoscaler/releases/tag/cluster-autoscaler-1.16.2) ([#84038](https://github.com/kubernetes/kubernetes/pull/84038), [@losipiuk](https://github.com/losipiuk)) [SIG Cluster Lifecycle]
-- Update to use go1.12.12 ([#84064](https://github.com/kubernetes/kubernetes/pull/84064), [@cblecker](https://github.com/cblecker)) [SIG Release and Testing]
- Upgrade to etcd client 3.3.17 to fix bug where etcd client does not parse IPv6 addresses correctly when members are joining, and to fix bug where failover on multi-member etcd cluster fails certificate check on DNS mismatch ([#83968](https://github.com/kubernetes/kubernetes/pull/83968), [@jpbetz](https://github.com/jpbetz)) [SIG API Machinery and Cloud Provider]`
const patchReleaseDeps = `## Dependencies
@@ -216,7 +215,6 @@ const patchReleaseExpectedHTML = `
Restores compatibility of kube-scheduler with clusters that do not enable the events.k8s.io/v1beta1 API (#84465, @yastij) [SIG API Machinery and Scheduling]
Switched intstr.Type to sized integer to follow API guidelines and improve compatibility with proto libraries (#83956, @liggitt) [SIG API Machinery]
Update Cluster Autoscaler version to 1.16.2 (CA release docs: https://github.com/kubernetes/autoscaler/releases/tag/cluster-autoscaler-1.16.2) (#84038, @losipiuk) [SIG Cluster Lifecycle]
-Update to use go1.12.12 (#84064, @cblecker) [SIG Release and Testing]
Upgrade to etcd client 3.3.17 to fix bug where etcd client does not parse IPv6 addresses correctly when members are joining, and to fix bug where failover on multi-member etcd cluster fails certificate check on DNS mismatch (#83968, @jpbetz) [SIG API Machinery and Cloud Provider]
Dependencies
diff --git a/cmd/krel/cmd/changelog_test.go b/cmd/krel/cmd/changelog_test.go
index 5354b67ec14..650dfbe8a8c 100644
--- a/cmd/krel/cmd/changelog_test.go
+++ b/cmd/krel/cmd/changelog_test.go
@@ -30,11 +30,12 @@ import (
func (s *sut) getChangelogOptions(tag string) *changelog.Options {
return &changelog.Options{
- RepoPath: s.repo.Dir(),
- ReplayDir: filepath.Join(testDataDir, "changelog-"+tag),
- Tag: tag,
- Tars: ".",
- Branch: git.DefaultBranch,
+ RepoPath: s.repo.Dir(),
+ ReplayDir: filepath.Join(testDataDir, "changelog-"+tag),
+ Tag: tag,
+ Tars: ".",
+ Branch: git.DefaultBranch,
+ CloneCVEMaps: false,
}
}
diff --git a/pkg/anago/stage.go b/pkg/anago/stage.go
index 91c7b53276c..067afff5ed8 100644
--- a/pkg/anago/stage.go
+++ b/pkg/anago/stage.go
@@ -491,6 +491,7 @@ func (d *DefaultStage) GenerateChangelog() error {
HTMLFile: releaseNotesHTMLFile,
JSONFile: releaseNotesJSONFile,
Dependencies: true,
+ CloneCVEMaps: true,
Tars: filepath.Join(
gitRoot,
fmt.Sprintf("%s-%s", release.BuildDir, d.state.versions.Prime()),
diff --git a/pkg/changelog/changelog.go b/pkg/changelog/changelog.go
index 91e4df1c968..3b1ff119c61 100644
--- a/pkg/changelog/changelog.go
+++ b/pkg/changelog/changelog.go
@@ -45,6 +45,8 @@ type Options struct {
JSONFile string
RecordDir string
ReplayDir string
+ CVEDataDir string
+ CloneCVEMaps bool
Dependencies bool
}
@@ -158,6 +160,14 @@ func (c *Changelog) Run() error {
}
}
} else {
+ if c.options.CloneCVEMaps {
+ cveDir, err := c.impl.CloneCVEData()
+ if err != nil {
+ return errors.Wrap(err, "getting cve data maps")
+ }
+ c.options.CVEDataDir = cveDir
+ }
+
// A patch version, let’s just use the previous patch
startTag := util.SemverToTagString(semver.Version{
Major: tag.Major, Minor: tag.Minor, Patch: tag.Patch - 1,
@@ -244,6 +254,12 @@ func (c *Changelog) generateReleaseNotes(
notesOptions.ReplayDir = c.options.ReplayDir
notesOptions.Pull = false
+ if c.options.CVEDataDir != "" {
+ notesOptions.MapProviderStrings = append(
+ notesOptions.MapProviderStrings, c.options.CVEDataDir,
+ )
+ }
+
if err := c.impl.ValidateAndFinish(notesOptions); err != nil {
return "", "", errors.Wrap(err, "validating notes options")
}
diff --git a/pkg/changelog/changelog_test.go b/pkg/changelog/changelog_test.go
index 06c9c187ede..59498475b3c 100644
--- a/pkg/changelog/changelog_test.go
+++ b/pkg/changelog/changelog_test.go
@@ -265,6 +265,32 @@ func TestRun(t *testing.T) {
},
shouldErr: true,
},
+ { // CloneCVEData returns error
+ prepare: func(mock *changelogfakes.FakeImpl, o *changelog.Options) {
+ o.CloneCVEMaps = true
+ mock.TagStringToSemverReturns(semver.Version{
+ Major: 1,
+ Minor: 19,
+ Patch: 3,
+ }, nil)
+ mock.CloneCVEDataReturns("", err)
+ },
+ shouldErr: true,
+ },
+ { // CloneCVEData returns empty string
+ prepare: func(mock *changelogfakes.FakeImpl, o *changelog.Options) {
+ o.CloneCVEMaps = true
+ mock.TagStringToSemverReturns(semver.Version{
+ Major: 1,
+ Minor: 19,
+ Patch: 3,
+ }, nil)
+ mock.ReadFileReturns([]byte(changelog.TocEnd), nil)
+ mock.GatherReleaseNotesReturns(¬es.ReleaseNotes{}, nil)
+ mock.CloneCVEDataReturns("", nil)
+ },
+ shouldErr: false,
+ },
} {
options := &changelog.Options{}
sut := changelog.New(options)
diff --git a/pkg/changelog/changelogfakes/fake_impl.go b/pkg/changelog/changelogfakes/fake_impl.go
index 39c26e4bf6f..6d9030b7224 100644
--- a/pkg/changelog/changelogfakes/fake_impl.go
+++ b/pkg/changelog/changelogfakes/fake_impl.go
@@ -1,5 +1,5 @@
/*
-Copyright The Kubernetes Authors.
+Copyright 2020 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -71,6 +71,18 @@ type FakeImpl struct {
checkoutReturnsOnCall map[int]struct {
result1 error
}
+ CloneCVEDataStub func() (string, error)
+ cloneCVEDataMutex sync.RWMutex
+ cloneCVEDataArgsForCall []struct {
+ }
+ cloneCVEDataReturns struct {
+ result1 string
+ result2 error
+ }
+ cloneCVEDataReturnsOnCall map[int]struct {
+ result1 string
+ result2 error
+ }
CommitStub func(*git.Repo, string) error
commitMutex sync.RWMutex
commitArgsForCall []struct {
@@ -567,6 +579,62 @@ func (fake *FakeImpl) CheckoutReturnsOnCall(i int, result1 error) {
}{result1}
}
+func (fake *FakeImpl) CloneCVEData() (string, error) {
+ fake.cloneCVEDataMutex.Lock()
+ ret, specificReturn := fake.cloneCVEDataReturnsOnCall[len(fake.cloneCVEDataArgsForCall)]
+ fake.cloneCVEDataArgsForCall = append(fake.cloneCVEDataArgsForCall, struct {
+ }{})
+ stub := fake.CloneCVEDataStub
+ fakeReturns := fake.cloneCVEDataReturns
+ fake.recordInvocation("CloneCVEData", []interface{}{})
+ fake.cloneCVEDataMutex.Unlock()
+ if stub != nil {
+ return stub()
+ }
+ if specificReturn {
+ return ret.result1, ret.result2
+ }
+ return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *FakeImpl) CloneCVEDataCallCount() int {
+ fake.cloneCVEDataMutex.RLock()
+ defer fake.cloneCVEDataMutex.RUnlock()
+ return len(fake.cloneCVEDataArgsForCall)
+}
+
+func (fake *FakeImpl) CloneCVEDataCalls(stub func() (string, error)) {
+ fake.cloneCVEDataMutex.Lock()
+ defer fake.cloneCVEDataMutex.Unlock()
+ fake.CloneCVEDataStub = stub
+}
+
+func (fake *FakeImpl) CloneCVEDataReturns(result1 string, result2 error) {
+ fake.cloneCVEDataMutex.Lock()
+ defer fake.cloneCVEDataMutex.Unlock()
+ fake.CloneCVEDataStub = nil
+ fake.cloneCVEDataReturns = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
+func (fake *FakeImpl) CloneCVEDataReturnsOnCall(i int, result1 string, result2 error) {
+ fake.cloneCVEDataMutex.Lock()
+ defer fake.cloneCVEDataMutex.Unlock()
+ fake.CloneCVEDataStub = nil
+ if fake.cloneCVEDataReturnsOnCall == nil {
+ fake.cloneCVEDataReturnsOnCall = make(map[int]struct {
+ result1 string
+ result2 error
+ })
+ }
+ fake.cloneCVEDataReturnsOnCall[i] = struct {
+ result1 string
+ result2 error
+ }{result1, result2}
+}
+
func (fake *FakeImpl) Commit(arg1 *git.Repo, arg2 string) error {
fake.commitMutex.Lock()
ret, specificReturn := fake.commitReturnsOnCall[len(fake.commitArgsForCall)]
@@ -2042,6 +2110,8 @@ func (fake *FakeImpl) Invocations() map[string][][]interface{} {
defer fake.addMutex.RUnlock()
fake.checkoutMutex.RLock()
defer fake.checkoutMutex.RUnlock()
+ fake.cloneCVEDataMutex.RLock()
+ defer fake.cloneCVEDataMutex.RUnlock()
fake.commitMutex.RLock()
defer fake.commitMutex.RUnlock()
fake.createDownloadsTableMutex.RLock()
diff --git a/pkg/changelog/const.go b/pkg/changelog/const.go
index 81ab8123991..c901f6e24c9 100644
--- a/pkg/changelog/const.go
+++ b/pkg/changelog/const.go
@@ -66,6 +66,24 @@ filename | sha512 hash
{{- end -}}
## Changelog since {{$PreviousRevision}}
+{{with .CVEList -}}
+## Important Security Information
+
+This release contains changes that address the following vulnerabilities:
+{{range .}}
+### {{.ID}}: {{.Title}}
+
+{{.Description}}
+
+**CVSS Rating:** {{.CVSSRating}} ({{.CVSSScore}}) [{{.CVSSVector}}]({{.CalcLink}})
+{{- if .TrackingIssue -}}
+
+**Tracking Issue:** {{.TrackingIssue}}
+{{- end }}
+
+{{ end }}
+{{- end -}}
+
{{with .NotesWithActionRequired -}}
## Urgent Upgrade Notes
diff --git a/pkg/changelog/impl.go b/pkg/changelog/impl.go
index 312aff3216d..a241907836f 100644
--- a/pkg/changelog/impl.go
+++ b/pkg/changelog/impl.go
@@ -23,16 +23,20 @@ import (
"text/template"
"github.com/blang/semver"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"sigs.k8s.io/mdtoc/pkg/mdtoc"
+ "k8s.io/release/pkg/cve"
"k8s.io/release/pkg/git"
"k8s.io/release/pkg/github"
"k8s.io/release/pkg/notes"
"k8s.io/release/pkg/notes/document"
"k8s.io/release/pkg/notes/options"
+ "k8s.io/release/pkg/object"
"sigs.k8s.io/release-utils/http"
"sigs.k8s.io/release-utils/util"
)
@@ -88,6 +92,7 @@ type impl interface {
Add(repo *git.Repo, filename string) error
Commit(repo *git.Repo, msg string) error
Rm(repo *git.Repo, force bool, files ...string) error
+ CloneCVEData() (cveDir string, err error)
}
type defaultImpl struct{}
@@ -215,3 +220,34 @@ func (*defaultImpl) Commit(repo *git.Repo, msg string) error {
func (*defaultImpl) Rm(repo *git.Repo, force bool, files ...string) error {
return repo.Rm(force, files...)
}
+
+// CloneCVEData copies the CVE data maps from the release bucket
+func (*defaultImpl) CloneCVEData() (cveDir string, err error) {
+ tmpdir, err := os.MkdirTemp(os.TempDir(), "cve-maps-")
+ if err != nil {
+ return "", errors.Wrap(err, "creating temporary dir for CVE data")
+ }
+
+ // Create a new GCS client
+ gcs := object.NewGCS()
+ remoteSrc, err := gcs.NormalizePath(object.GcsPrefix + filepath.Join(cve.Bucket, cve.Directory))
+ if err != nil {
+ return "", errors.Wrap(err, "normalizing cve bucket path")
+ }
+
+ bucketExists, err := gcs.PathExists(remoteSrc)
+ if err != nil {
+ return "", errors.Wrap(err, "checking if CVE directory exists")
+ }
+ if !bucketExists {
+ logrus.Warnf("CVE data maps not found in bucket location: %s", remoteSrc)
+ return "", nil
+ }
+
+ // Rsync the CVE files to the temp dir
+ if err := gcs.RsyncRecursive(remoteSrc, tmpdir); err != nil {
+ return "", errors.Wrap(err, "copying release directory to bucket")
+ }
+ logrus.Infof("Successfully synchronized CVE data maps from %s", remoteSrc)
+ return tmpdir, nil
+}
diff --git a/pkg/notes/document/document.go b/pkg/notes/document/document.go
index cfa4318360a..fc3bf6d460a 100644
--- a/pkg/notes/document/document.go
+++ b/pkg/notes/document/document.go
@@ -217,11 +217,6 @@ func New(
for _, pr := range releaseNotes.History() {
note := releaseNotes.Get(pr)
- if note.DoNotPublish {
- logrus.Debugf("skipping PR %d as (marked to not be published)", pr)
- continue
- }
-
if _, hasCVE := note.DataFields["cve"]; hasCVE {
logrus.Infof("Release note for PR #%d has CVE vulnerability info", note.PrNumber)
@@ -240,6 +235,11 @@ func New(
doc.CVEList = append(doc.CVEList, newcve)
}
+ if note.DoNotPublish {
+ logrus.Debugf("skipping PR %d as (marked to not be published)", pr)
+ continue
+ }
+
// TODO: Refactor the logic here and add testing.
if note.DuplicateKind {
kind := mapKind(highestPriorityKind(note.Kinds))
diff --git a/pkg/notes/notes.go b/pkg/notes/notes.go
index 5274d9ae28c..2c5a97bf937 100644
--- a/pkg/notes/notes.go
+++ b/pkg/notes/notes.go
@@ -127,7 +127,7 @@ type ReleaseNote struct {
DoNotPublish bool `json:"do_not_publish,omitempty"`
// DataFields a key indexed map of data fields
- DataFields map[string]ReleaseNotesDataField `json:"data_fields,omitempty"`
+ DataFields map[string]ReleaseNotesDataField `json:"-"`
}
type Documentation struct {
@@ -269,11 +269,45 @@ func (g *Gatherer) ListReleaseNotes() (*ReleaseNotes, error) {
return nil, errors.Wrap(err, "listing commits")
}
- results, err := g.gatherNotes(commits)
+ // Get the PRs into a temporary results set
+ resultsTemp, err := g.gatherNotes(commits)
if err != nil {
return nil, errors.Wrap(err, "gathering notes")
}
+ // Cycle the results and add the complete notes, as well as those that
+ // have a map associated with it
+ results := []*Result{}
+ logrus.Info("Checking PRs for mapped data")
+ for _, res := range resultsTemp {
+ // If the PR has no release note, check if we have to add it
+ if MatchesExcludeFilter(*res.pullRequest.Body) {
+ for _, provider := range mapProviders {
+ noteMaps, err := provider.GetMapsForPR(res.pullRequest.GetNumber())
+ if err != nil {
+ return nil, errors.Wrapf(
+ err, "checking if a map exists for PR %d", res.pullRequest.GetNumber(),
+ )
+ }
+ if len(noteMaps) != 0 {
+ logrus.Infof(
+ "Artificially adding pr #%d because a map for it was found",
+ res.pullRequest.GetNumber(),
+ )
+ results = append(results, res)
+ } else {
+ logrus.Debugf(
+ "Skipping PR #%d because it contains no release note",
+ res.pullRequest.GetNumber(),
+ )
+ }
+ }
+ } else {
+ // Append the note as it is
+ results = append(results, res)
+ }
+ }
+
dedupeCache := map[string]struct{}{}
notes := NewReleaseNotes()
for _, result := range results {
@@ -311,7 +345,6 @@ func (g *Gatherer) ListReleaseNotes() (*ReleaseNotes, error) {
dedupeCache[note.Text] = struct{}{}
}
}
-
return notes, nil
}
@@ -676,14 +709,6 @@ func (g *Gatherer) notesForCommit(commit *gogithub.RepositoryCommit) (*Result, e
"Got PR #%d for commit: %s", pr.GetNumber(), commit.GetSHA(),
)
- if MatchesExcludeFilter(prBody) {
- logrus.Debugf(
- "Skipping PR #%d because it contains no release note",
- pr.GetNumber(),
- )
- continue
- }
-
if MatchesIncludeFilter(prBody) {
res := &Result{commit: commit, pullRequest: pr}
logrus.Infof("PR #%d seems to contain a release note", pr.GetNumber())
diff --git a/pkg/notes/notes_gatherer_test.go b/pkg/notes/notes_gatherer_test.go
index be5a5cd1b69..052d2ea911a 100644
--- a/pkg/notes/notes_gatherer_test.go
+++ b/pkg/notes/notes_gatherer_test.go
@@ -370,7 +370,7 @@ func TestGatherNotes(t *testing.T) {
resultsChecker: func(t *testing.T, results []*Result) {
// there is not much we can check on the Result, as all the fields are
// unexported
- expectedResultSize := 7
+ expectedResultSize := 13
if e, a := expectedResultSize, len(results); e != a {
t.Errorf("Expected the result to be of size %d, got %d", e, a)
}
diff --git a/pkg/notes/options/options.go b/pkg/notes/options/options.go
index 3d0dfd50ad8..3455066bfda 100644
--- a/pkg/notes/options/options.go
+++ b/pkg/notes/options/options.go
@@ -146,13 +146,14 @@ const (
// New creates a new Options instance with the default values
func New() *Options {
return &Options{
- DiscoverMode: RevisionDiscoveryModeNONE,
- GithubOrg: git.DefaultGithubOrg,
- GithubRepo: git.DefaultGithubRepo,
- Format: FormatMarkdown,
- GoTemplate: GoTemplateDefault,
- Pull: true,
- gitCloneFn: git.CloneOrOpenGitHubRepo,
+ DiscoverMode: RevisionDiscoveryModeNONE,
+ GithubOrg: git.DefaultGithubOrg,
+ GithubRepo: git.DefaultGithubRepo,
+ Format: FormatMarkdown,
+ GoTemplate: GoTemplateDefault,
+ Pull: true,
+ gitCloneFn: git.CloneOrOpenGitHubRepo,
+ MapProviderStrings: []string{},
}
}