Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature to opt-in to get notifications for beta releases #11169

Merged
merged 9 commits into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/minikube/cmd/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ var settings = []Setting{
name: config.WantUpdateNotification,
set: SetBool,
},
{
name: config.WantBetaUpdateNotification,
set: SetBool,
},
{
name: config.ReminderWaitPeriodInHours,
set: SetInt,
Expand Down
2 changes: 2 additions & 0 deletions deploy/minikube/releases-beta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[
]
47 changes: 29 additions & 18 deletions hack/jenkins/release_update_releases_json.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ export DARWIN_SHA256=$(cat out/minikube-darwin-amd64.sha256)
export LINUX_SHA256=$(cat out/minikube-linux-amd64.sha256)
export WINDOWS_SHA256=$(cat out/minikube-windows-amd64.exe.sha256)

if ! [[ "${VERSION_BUILD}" =~ ^[0-9]+$ ]]; then
echo "NOTE: ${TAGNAME} appears to be a non-standard release, not updating releases.json"
exit 0
fi

# Update releases.json w/ new release in gcs and github
git config user.name "minikube-bot"
git config user.email "minikube-bot@google.com"
Expand All @@ -43,20 +38,36 @@ git checkout -b "jenkins-releases.json-${TAGNAME}"

git status

#Prepends the new version to the release.json file
sed -i "0,/{/s/{/{\n \"name\": \"${TAGNAME}\",\n \"checksums\": {\n \"darwin\": \"${DARWIN_SHA256}\",\n \"linux\": \"${LINUX_SHA256}\",\n \"windows\": \"${WINDOWS_SHA256}\"\n }\n },\n {"/ deploy/minikube/releases.json
if ! [[ "${VERSION_BUILD}" =~ ^[0-9]+$ ]]; then
#Prepends the new version to the release-beta.json file
sed -i "0,/{/s/{/{\n \"name\": \"${TAGNAME}\",\n \"checksums\": {\n \"darwin\": \"${DARWIN_SHA256}\",\n \"linux\": \"${LINUX_SHA256}\",\n \"windows\": \"${WINDOWS_SHA256}\"\n }\n },\n {"/ deploy/minikube/releases-beta.json

git add -A
git commit -m "Update releases-beta.json to include ${TAGNAME}"
git remote add minikube-bot git@github.com:minikube-bot/minikube.git
git push -f minikube-bot jenkins-releases.json-${TAGNAME}

# Send PR from minikube-bot/minikube to kubernetes/minikube
curl -X POST -u minikube-bot:${BOT_PASSWORD} -k -d "{\"title\": \"update releases-beta.json to include ${TAGNAME}\",\"head\": \"minikube-bot:jenkins-releases.json-${TAGNAME}\",\"base\": \"master\"}" https://api.github.com/repos/kubernetes/minikube/pulls

#Update the front page of our documentation
now=$(date +"%b %d, %Y")
sed -i "s/Latest Release: .* (/Latest Release: ${TAGNAME} - ${now} (/" site/content/en/docs/_index.md
# Upload file to GCS so that minikube can see the new version
gsutil cp deploy/minikube/releases.json gs://minikube/releases-beta.json
else
#Prepends the new version to the release.json file
sed -i "0,/{/s/{/{\n \"name\": \"${TAGNAME}\",\n \"checksums\": {\n \"darwin\": \"${DARWIN_SHA256}\",\n \"linux\": \"${LINUX_SHA256}\",\n \"windows\": \"${WINDOWS_SHA256}\"\n }\n },\n {"/ deploy/minikube/releases.json

git add -A
git commit -m "Update releases.json to include ${TAGNAME}"
git remote add minikube-bot git@github.com:minikube-bot/minikube.git
git push -f minikube-bot jenkins-releases.json-${TAGNAME}
#Update the front page of our documentation
now=$(date +"%b %d, %Y")
sed -i "s/Latest Release: .* (/Latest Release: ${TAGNAME} - ${now} (/" site/content/en/docs/_index.md

# Send PR from minikube-bot/minikube to kubernetes/minikube
curl -X POST -u minikube-bot:${BOT_PASSWORD} -k -d "{\"title\": \"update releases.json to include ${TAGNAME}\",\"head\": \"minikube-bot:jenkins-releases.json-${TAGNAME}\",\"base\": \"master\"}" https://api.github.com/repos/kubernetes/minikube/pulls
git add -A
git commit -m "Update releases.json to include ${TAGNAME}"
git remote add minikube-bot git@github.com:minikube-bot/minikube.git
git push -f minikube-bot jenkins-releases.json-${TAGNAME}

# Upload file to GCS so that minikube can see the new version
gsutil cp deploy/minikube/releases.json gs://minikube/releases.json
# Send PR from minikube-bot/minikube to kubernetes/minikube
curl -X POST -u minikube-bot:${BOT_PASSWORD} -k -d "{\"title\": \"update releases.json to include ${TAGNAME}\",\"head\": \"minikube-bot:jenkins-releases.json-${TAGNAME}\",\"base\": \"master\"}" https://api.github.com/repos/kubernetes/minikube/pulls

# Upload file to GCS so that minikube can see the new version
gsutil cp deploy/minikube/releases.json gs://minikube/releases.json
fi
4 changes: 3 additions & 1 deletion pkg/minikube/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ import (
const (
// WantUpdateNotification is the key for WantUpdateNotification
WantUpdateNotification = "WantUpdateNotification"
// ReminderWaitPeriodInHours is the key for WantUpdateNotification
// WantBetaUpdateNotification is the key for WantBetaUpdateNotification
WantBetaUpdateNotification = "WantBetaUpdateNotification"
// ReminderWaitPeriodInHours is the key for ReminderWaitPeriodInHours
ReminderWaitPeriodInHours = "ReminderWaitPeriodInHours"
// WantReportError is the key for WantReportError
WantReportError = "WantReportError"
Expand Down
2 changes: 2 additions & 0 deletions pkg/minikube/notify/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ package notify
const (
// GithubMinikubeReleasesURL is the URL of the minikube github releases JSON file
GithubMinikubeReleasesURL = "https://storage.googleapis.com/minikube/releases.json"
// GithubMinikubeBetaReleasesURL is the URL of the minikube Github beta releases JSON file
GithubMinikubeBetaReleasesURL = "https://storage.googleapis.com/minikube/releases-beta.json"
)
83 changes: 63 additions & 20 deletions pkg/minikube/notify/notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,35 +43,70 @@ var (
)

// MaybePrintUpdateTextFromGithub prints update text if needed, from github
func MaybePrintUpdateTextFromGithub() bool {
return MaybePrintUpdateText(GithubMinikubeReleasesURL, lastUpdateCheckFilePath)
func MaybePrintUpdateTextFromGithub() {
maybePrintUpdateText(GithubMinikubeReleasesURL, GithubMinikubeBetaReleasesURL, lastUpdateCheckFilePath)
}

// MaybePrintUpdateText prints update text, returns a bool if life is good.
func MaybePrintUpdateText(url string, lastUpdatePath string) bool {
func maybePrintUpdateText(latestReleasesURL string, betaReleasesURL string, lastUpdatePath string) {
if !shouldCheckURLVersion(lastUpdatePath) {
return false
return
}
latestVersion, err := getLatestVersionFromURL(url)
latestVersion, err := getLatestVersionFromURL(latestReleasesURL)
if err != nil {
klog.Warning(err)
return true
return
}
localVersion, err := version.GetSemverVersion()
if err != nil {
klog.Warning(err)
return true
}
if localVersion.Compare(latestVersion) < 0 {
if err := writeTimeToFile(lastUpdateCheckFilePath, time.Now().UTC()); err != nil {
klog.Errorf("write time failed: %v", err)
}
url := "https://github.com/kubernetes/minikube/releases/tag/v" + latestVersion.String()
out.Styled(style.Celebrate, `minikube {{.version}} is available! Download it: {{.url}}`, out.V{"version": latestVersion, "url": url})
out.Styled(style.Tip, "To disable this notice, run: 'minikube config set WantUpdateNotification false'\n")
return true
}
return false
return
}
if maybePrintBetaUpdateText(betaReleasesURL, localVersion, latestVersion, lastUpdatePath) {
return
}
if localVersion.Compare(latestVersion) >= 0 {
return
}
printUpdateText(latestVersion)
}

// maybePrintBetaUpdateText returns true if update text is printed
func maybePrintBetaUpdateText(betaReleasesURL string, localVersion semver.Version, latestFullVersion semver.Version, lastUpdatePath string) bool {
if !shouldCheckURLBetaVersion(lastUpdatePath) {
return false
}
latestBetaVersion, err := getLatestVersionFromURL(betaReleasesURL)
if err != nil {
klog.Warning(err)
return false
}
if latestFullVersion.Compare(latestBetaVersion) >= 0 {
return false
}
if localVersion.Compare(latestBetaVersion) >= 0 {
return false
}
printBetaUpdateText(latestBetaVersion)
return true
}

func printUpdateTextCommon(version semver.Version) {
if err := writeTimeToFile(lastUpdateCheckFilePath, time.Now().UTC()); err != nil {
klog.Errorf("write time failed: %v", err)
}
url := "https://github.com/kubernetes/minikube/releases/tag/v" + version.String()
out.Styled(style.Celebrate, `minikube {{.version}} is available! Download it: {{.url}}`, out.V{"version": version, "url": url})
}

func printUpdateText(version semver.Version) {
printUpdateTextCommon(version)
out.Styled(style.Tip, "To disable this notice, run: 'minikube config set WantUpdateNotification false'\n")
}

func printBetaUpdateText(version semver.Version) {
printUpdateTextCommon(version)
out.Styled(style.Tip, "To disable beta notices, run: 'minikube config set WantBetaUpdateNotification false'")
out.Styled(style.Tip, "To disable update notices in general, run: 'minikube config set WantUpdateNotification false'\n")
}

func shouldCheckURLVersion(filePath string) bool {
Expand All @@ -82,6 +117,14 @@ func shouldCheckURLVersion(filePath string) bool {
return time.Since(lastUpdateTime).Hours() >= viper.GetFloat64(config.ReminderWaitPeriodInHours)
}

func shouldCheckURLBetaVersion(filePath string) bool {
if !viper.GetBool(config.WantBetaUpdateNotification) {
return false
}

return shouldCheckURLVersion(filePath)
}

// Release represents a release
type Release struct {
Name string
Expand Down Expand Up @@ -112,7 +155,7 @@ func getJSON(url string, target *Releases) error {
return json.NewDecoder(resp.Body).Decode(target)
}

func getLatestVersionFromURL(url string) (semver.Version, error) {
var getLatestVersionFromURL = func(url string) (semver.Version, error) {
r, err := GetAllVersionsFromURL(url)
if err != nil {
return semver.Version{}, err
Expand Down
135 changes: 70 additions & 65 deletions pkg/minikube/notify/notify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"time"

Expand All @@ -35,13 +36,7 @@ import (
"k8s.io/minikube/pkg/version"
)

func TestMaybePrintUpdateTextFromGithub(t *testing.T) {
if MaybePrintUpdateTextFromGithub() {
t.Fatal("MaybePrintUpdateTextFromGithub() expected to return false for basic setup, bot got true")
}
}

func TestShouldCheckURL(t *testing.T) {
func TestShouldCheckURLVersion(t *testing.T) {
tempDir := tests.MakeTempDir()
defer tests.RemoveTempDir(tempDir)

Expand Down Expand Up @@ -80,6 +75,26 @@ func TestShouldCheckURL(t *testing.T) {

}

func TestShouldCheckURLBetaVersion(t *testing.T) {
tempDir := tests.MakeTempDir()
defer tests.RemoveTempDir(tempDir)

lastUpdateCheckFilePath := filepath.Join(tempDir, "last_update_check")
viper.Set(config.WantUpdateNotification, true)

// test if the user disables beta update notification in config, the URL version does not get checked
viper.Set(config.WantBetaUpdateNotification, false)
if shouldCheckURLBetaVersion(lastUpdateCheckFilePath) {
t.Fatalf("shouldCheckURLBetaVersion returned true even though config had WantBetaUpdateNotification: false")
}

// test if the user enables beta update notification in config, the URL version does get checked
viper.Set(config.WantBetaUpdateNotification, true)
if !shouldCheckURLBetaVersion(lastUpdateCheckFilePath) {
t.Fatalf("shouldCheckURLBetaVersion returned false even though config had WantBetaUpdateNotification: true")
}
}

type URLHandlerCorrect struct {
releases Releases
}
Expand Down Expand Up @@ -150,87 +165,77 @@ func TestGetLatestVersionFromURLMalformed(t *testing.T) {
}
}

var mockGetLatestVersionFromURL = func(url string) (semver.Version, error) {
return semver.Make(url)
}

func TestMaybePrintUpdateText(t *testing.T) {
getLatestVersionFromURL = mockGetLatestVersionFromURL

tempDir := tests.MakeTempDir()
defer tests.RemoveTempDir(tempDir)
outputBuffer := tests.NewFakeFile()
out.SetOutFile(outputBuffer)

var tc = []struct {
len int
wantUpdateNotification bool
latestVersionFromURL string
description string
status bool
url string
lastUpdateCheckFilePath string
wantUpdateNotification bool
wantBetaUpdateNotification bool
latestFullVersionFromURL string
latestBetaVersionFromURL string
description string
want string
}{
{
len: 1,
latestVersionFromURL: "0.0.0-dev",
wantUpdateNotification: true,
description: "latest version lower or equal",
wantUpdateNotification: true,
wantBetaUpdateNotification: true,
latestFullVersionFromURL: "99.0.0",
latestBetaVersionFromURL: "99.0.0-beta.0",
description: "latest full version greater",
want: "99.0.0 ",
},
{
len: 0,
latestVersionFromURL: "100.0.0-dev",
wantUpdateNotification: true,
description: "latest version greater",
status: true,
wantUpdateNotification: true,
wantBetaUpdateNotification: true,
latestFullVersionFromURL: "97.0.0",
latestBetaVersionFromURL: "98.0.0-beta.0",
description: "latest beta version greater",
want: "98.0.0-beta.0",
},
{
len: 1,
latestVersionFromURL: "100.0.0-dev",
wantUpdateNotification: false,
description: "notification unwanted",
wantUpdateNotification: false,
wantBetaUpdateNotification: true,
latestFullVersionFromURL: "97.0.0",
latestBetaVersionFromURL: "96.0.0-beta.0",
description: "notification unwanted",
},
{
len: 1,
latestVersionFromURL: "100.0.0-dev",
wantUpdateNotification: true,
description: "bad url",
url: "this is not valid url",
status: true,
},
{
len: 1,
latestVersionFromURL: "10.0.0-dev",
wantUpdateNotification: true,
description: "bad lastUpdateCheckFilePath",
lastUpdateCheckFilePath: "/etc/passwd",
status: true,
wantUpdateNotification: true,
wantBetaUpdateNotification: false,
latestFullVersionFromURL: "0.0.0-unset",
latestBetaVersionFromURL: "95.0.0-beta.0",
description: "beta notification unwanted",
},
}

viper.Set(config.ReminderWaitPeriodInHours, 24)
for _, test := range tc {
t.Run(test.description, func(t *testing.T) {
viper.Set(config.WantUpdateNotification, test.wantUpdateNotification)
for _, tt := range tc {
t.Run(tt.description, func(t *testing.T) {
outputBuffer := tests.NewFakeFile()
out.SetOutFile(outputBuffer)

viper.Set(config.WantUpdateNotification, tt.wantUpdateNotification)
viper.Set(config.WantBetaUpdateNotification, tt.wantBetaUpdateNotification)
lastUpdateCheckFilePath = filepath.Join(tempDir, "last_update_check")
if test.lastUpdateCheckFilePath != "" {
lastUpdateCheckFilePath = test.lastUpdateCheckFilePath
}
latestVersionFromURL := test.latestVersionFromURL
handler := &URLHandlerCorrect{
releases: []Release{{Name: version.VersionPrefix + latestVersionFromURL}},
}
server := httptest.NewServer(handler)
defer server.Close()
if test.url == "" {
test.url = server.URL
}

tmpfile, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("Cannot create temp file: %v", err)
}
defer os.Remove(tmpfile.Name())
status := MaybePrintUpdateText(test.url, tmpfile.Name())
if test.status != status {
t.Fatalf("MaybePrintUpdateText expected to return %v, but got %v", test.status, status)
}
if len(outputBuffer.String()) == test.len {
t.Fatalf("Expected MaybePrintUpdateText to output text as the current version is %s and version %s was served from URL but output was [%s]",
version.GetVersion(), latestVersionFromURL, outputBuffer.String())

maybePrintUpdateText(tt.latestFullVersionFromURL, tt.latestBetaVersionFromURL, tmpfile.Name())
got := outputBuffer.String()
if (tt.want == "" && len(got) != 0) || (tt.want != "" && !strings.Contains(got, tt.want)) {
t.Fatalf("Expected MaybePrintUpdateText to contain the text %q as the current version is %s and full version %s and beta version %s, but output was [%s]",
tt.want, version.GetVersion(), tt.latestFullVersionFromURL, tt.latestBetaVersionFromURL, outputBuffer.String())
}
})
}
Expand Down
Loading