-
Notifications
You must be signed in to change notification settings - Fork 80
/
plugin_version_checker.go
128 lines (114 loc) · 3.93 KB
/
plugin_version_checker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package cmd
import (
"encoding/json"
"fmt"
"github.com/hashicorp/go-retryablehttp"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
// VersionChecker interface to help us mock test the version checker
type VersionChecker interface {
getLatestPluginVersion() (string, error)
}
// RealVersionChecker will do actual call to GitHub API to get version
type RealVersionChecker struct{}
// KubectlFbReleaseURL is the public GitHub URL we read the latest version from
const KubectlFbReleaseURL = "https://api.github.com/repos/FoundationDB/fdb-kubernetes-operator/releases/latest"
// LocalTempVersionFileName is where we cache plugin version for 24 hours, so we don't call GitHub for every single command(also rate-limit issue on GitHub api calls)
const LocalTempVersionFileName = "kubectl-fdb-version.txt"
// PluginVersionDetails Contains [partial] plugin version details from GitHub
type PluginVersionDetails struct {
ID int64 `json:"id"`
Version string `json:"tag_name"`
Name string `json:"name"`
}
// It reads the latest fdb plugin version from local temp file,
// if not exists, gets it from GitHub and store it locally and then returns it
func (versionChecker *RealVersionChecker) getLatestPluginVersion() (string, error) {
fileName := filepath.Join(os.TempDir(), LocalTempVersionFileName)
_, err := os.Stat(fileName)
if os.IsNotExist(err) {
return updateLocalVersion(fileName)
}
if isVersionFileCreatedToday(fileName) {
return readVersionFromLocalFile(fileName)
}
return updateLocalVersion(fileName)
}
// readVersionFromLocalFile reads the version value from the local temp file
func readVersionFromLocalFile(fileName string) (string, error) {
file, _ := os.OpenFile(fileName, os.O_RDONLY, 0644)
defer file.Close()
content, err := os.ReadFile(fileName)
if err != nil {
return "", err
}
return string(content), err
}
// Check if the local temp version file is created within the last 24 hours
func isVersionFileCreatedToday(filename string) bool {
fileInfo, err := os.Stat(filename)
if err != nil {
return false
}
return time.Since(fileInfo.ModTime()) <= 24*time.Hour
}
// write version value in local temp file
func writeVersionToLocalTempFile(fileName string, version string) (int, error) {
file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
// Handle the error if the file cannot be opened.
panic(err)
}
defer file.Close()
return file.WriteString(version)
}
// read version from HitHub and update local version file
func updateLocalVersion(fileName string) (string, error) {
latestVersion, err := readVersionFromGitHub()
if err != nil {
return "", err
}
_, err = writeVersionToLocalTempFile(fileName, latestVersion)
return latestVersion, err
}
// read the latest release version number from GitHub, due to GitHub api rate limit we don't do it for every command
func readVersionFromGitHub() (string, error) {
retryClient := retryablehttp.NewClient()
retryClient.RetryMax = 2
retryClient.RetryWaitMax = 1 * time.Second
retryClient.Logger = nil
retryClient.CheckRetry = retryablehttp.ErrorPropagatedRetryPolicy
retryClient.HTTPClient.Timeout = 1 * time.Second
req, _ := retryablehttp.NewRequest(http.MethodGet, KubectlFbReleaseURL, nil)
resp, err := retryClient.Do(req)
if err != nil || resp == nil {
fmt.Println("Failed to fetch kubectl-fdb version from GitHub")
return "", err
}
if resp == nil {
return "", nil
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error in reading version from GitHub")
return "", err
}
var result = PluginVersionDetails{}
if !strings.Contains(strings.ToUpper(resp.Status), "OK") {
fmt.Println("Failed to get parse version info from GitHub response")
return "", err
}
err = json.Unmarshal(body, &result)
if err != nil {
fmt.Println("Failed to read version from GitHub response\n", err)
return "", err
}
//removing 'v' from beginning of release version
return result.Version[1:], nil
}