-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Blaize Kaye
committed
Apr 14, 2024
1 parent
b0ae450
commit ba23bc3
Showing
3 changed files
with
386 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package handler | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"os" | ||
) | ||
|
||
// eolfunctions.go contains all the basic functionality for checking key facts' end of life status against https://endoflife.date/docs/api | ||
|
||
const eloCacheLocation = "/tmp/eol.cache" | ||
|
||
type PackageInfo struct { | ||
Cycle string `json:"cycle"` | ||
ReleaseDate string `json:"releaseDate"` | ||
EOL string `json:"eol"` | ||
Latest string `json:"latest"` | ||
LatestReleaseDate string `json:"latestReleaseDate"` | ||
Link string `json:"link"` | ||
LTS bool `json:"lts"` | ||
} | ||
|
||
type EOLData struct { | ||
Packages map[string][]PackageInfo | ||
CacheLocation string | ||
} | ||
|
||
type NewEOLDataArgs struct { | ||
Packages []string | ||
CacheLocation string | ||
PreventCacheRefresh bool | ||
ForceCacheRefresh bool | ||
} | ||
|
||
// NewEOLData creates a new EOLData struct with end-of-life information for the provided Packages. | ||
func NewEOLData(args NewEOLDataArgs) (*EOLData, error) { | ||
|
||
// basic assertions of logic | ||
if args.ForceCacheRefresh && !args.PreventCacheRefresh { | ||
return nil, fmt.Errorf("You cannot Force Cache Refresh AND Prevent Cache Refresh") | ||
} | ||
|
||
packages := args.Packages | ||
cacheLocation := args.CacheLocation | ||
data := &EOLData{ | ||
CacheLocation: cacheLocation, | ||
} | ||
|
||
// Check if cache file exists | ||
if _, err := os.Stat(data.CacheLocation); err == nil || args.ForceCacheRefresh { | ||
// Cache file exists, load data from file | ||
if err := loadDataFromFile(data.CacheLocation, data); err != nil { | ||
return nil, err | ||
} | ||
} else if os.IsNotExist(err) { | ||
// Cache file does not exist, fetch data and write to file | ||
endOfLifeInfo := GetEndOfLifeInfo(packages) | ||
data.Packages = endOfLifeInfo | ||
|
||
// Write to cache file | ||
if err := writeDataToFile(data.CacheLocation, data); err != nil { | ||
return nil, err | ||
} | ||
} else { | ||
// Some other error occurred | ||
return nil, err | ||
} | ||
|
||
return data, nil | ||
} | ||
|
||
func GetEndOfLifeInfo(packageNames []string) map[string][]PackageInfo { | ||
endOfLifeInfo := make(map[string][]PackageInfo) | ||
|
||
for _, packageName := range packageNames { | ||
url := fmt.Sprintf("https://endoflife.date/api/%s.json", packageName) | ||
response, err := http.Get(url) | ||
if err != nil { | ||
fmt.Printf("Error getting end of life info for %s: %v\n", packageName, err) | ||
continue | ||
} | ||
defer response.Body.Close() | ||
|
||
body, err := ioutil.ReadAll(response.Body) | ||
if err != nil { | ||
fmt.Printf("Error reading response body for %s: %v\n", packageName, err) | ||
continue | ||
} | ||
|
||
var data []PackageInfo | ||
if err := json.Unmarshal(body, &data); err != nil { | ||
fmt.Printf("Error parsing JSON for %s: %v\n", packageName, err) | ||
continue | ||
} | ||
|
||
// Assuming the API returns an array of PackageInfo | ||
if len(data) > 0 { | ||
endOfLifeInfo[packageName] = data // Assuming we're interested in the first entry | ||
} | ||
} | ||
|
||
return endOfLifeInfo | ||
} | ||
|
||
// loadDataFromFile loads data from a file into an EOLData struct. | ||
func loadDataFromFile(filename string, data *EOLData) error { | ||
file, err := os.Open(filename) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
decoder := json.NewDecoder(file) | ||
if err := decoder.Decode(data); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// writeDataToFile writes data from an EOLData struct to a file. | ||
func writeDataToFile(filename string, data *EOLData) error { | ||
jsonData, err := json.MarshalIndent(data, "", " ") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := ioutil.WriteFile(filename, jsonData, 0644); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package handler | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
func TestGetEndOfLifeInfo(t *testing.T) { | ||
type args struct { | ||
packageNames []string | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
wantResponse bool | ||
}{ | ||
{ | ||
name: "Get alpine information", | ||
args: args{packageNames: []string{ | ||
"alpine", | ||
"ubuntu", | ||
}}, | ||
wantResponse: true, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got := GetEndOfLifeInfo(tt.args.packageNames) | ||
|
||
if tt.wantResponse == true { | ||
for _, name := range tt.args.packageNames { | ||
if len(got[name]) == 0 { | ||
t.Errorf("Expected data for package %v, got nothing", name) | ||
} | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestNewEOLData(t *testing.T) { | ||
type args struct { | ||
EolArgs NewEOLDataArgs | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want *EOLData | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "Test No Cache", | ||
args: args{ | ||
EolArgs: NewEOLDataArgs{ | ||
Packages: []string{ | ||
"alpine", | ||
}, | ||
CacheLocation: "testnocache.json", | ||
}, | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
// Let's set up a temporary location to save the incoming data | ||
dir, err := os.MkdirTemp("", "*-test") | ||
if err != nil { | ||
t.Errorf("Unable to create test directory") | ||
return | ||
} | ||
defer func(path string) { | ||
err := os.RemoveAll(path) | ||
if err != nil { | ||
fmt.Println("Unable to remove directory: ", path) | ||
} | ||
}(dir) | ||
tt.args.EolArgs.CacheLocation = filepath.Join(dir, tt.args.EolArgs.CacheLocation) | ||
got, err := NewEOLData(tt.args.EolArgs) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("NewEOLData() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if len(got.Packages[tt.args.EolArgs.Packages[0]]) == 0 { | ||
t.Errorf("Could not find any Packages for package '%v'\n", tt.args.EolArgs.Packages[0]) | ||
} | ||
|
||
}) | ||
} | ||
} | ||
|
||
func TestNewEOLDataWithExistingCache(t *testing.T) { | ||
type args struct { | ||
EolArgs NewEOLDataArgs | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want *EOLData | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "Test No Cache", | ||
args: args{ | ||
EolArgs: NewEOLDataArgs{ | ||
Packages: []string{ | ||
"alpine", | ||
}, | ||
CacheLocation: "testassets/EOLdata/testnocache.json", | ||
}, | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
// Let's set up a temporary location to save the incoming data | ||
got, err := NewEOLData(tt.args.EolArgs) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("NewEOLData() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if len(got.Packages[tt.args.EolArgs.Packages[0]]) == 0 { | ||
t.Errorf("Could not find any Packages for package '%v'\n", tt.args.EolArgs.Packages[0]) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
{ | ||
"Packages": { | ||
"alpine": [ | ||
{ | ||
"cycle": "3.19", | ||
"releaseDate": "2023-12-07", | ||
"eol": "2025-11-01", | ||
"latest": "3.19.1", | ||
"latestReleaseDate": "2024-01-26", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.19.1-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.18", | ||
"releaseDate": "2023-05-09", | ||
"eol": "2025-05-09", | ||
"latest": "3.18.6", | ||
"latestReleaseDate": "2024-01-26", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.16.9-3.17.7-3.18.6-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.17", | ||
"releaseDate": "2022-11-22", | ||
"eol": "2024-11-22", | ||
"latest": "3.17.7", | ||
"latestReleaseDate": "2024-01-26", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.16.9-3.17.7-3.18.6-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.16", | ||
"releaseDate": "2022-05-23", | ||
"eol": "2024-05-23", | ||
"latest": "3.16.9", | ||
"latestReleaseDate": "2024-01-26", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.16.9-3.17.7-3.18.6-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.15", | ||
"releaseDate": "2021-11-24", | ||
"eol": "2023-11-01", | ||
"latest": "3.15.11", | ||
"latestReleaseDate": "2023-11-30", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.15.10-3.16.7-3.17.5-3.18.3-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.14", | ||
"releaseDate": "2021-06-15", | ||
"eol": "2023-05-01", | ||
"latest": "3.14.10", | ||
"latestReleaseDate": "2023-03-29", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.14.10-3.15.8-3.16.5-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.13", | ||
"releaseDate": "2021-01-14", | ||
"eol": "2022-11-01", | ||
"latest": "3.13.12", | ||
"latestReleaseDate": "2022-08-09", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.12.12-3.13.10-3.14.6-3.15.4-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.12", | ||
"releaseDate": "2020-05-29", | ||
"eol": "2022-05-01", | ||
"latest": "3.12.12", | ||
"latestReleaseDate": "2022-04-04", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.12.12-3.13.10-3.14.6-3.15.4-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.11", | ||
"releaseDate": "2019-12-19", | ||
"eol": "2021-11-01", | ||
"latest": "3.11.13", | ||
"latestReleaseDate": "2021-11-12", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.11.13-3.12.9-3.13.7-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.10", | ||
"releaseDate": "2019-06-19", | ||
"eol": "2021-05-01", | ||
"latest": "3.10.9", | ||
"latestReleaseDate": "2021-04-14", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.10.9-3.11.11-3.12.7-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.9", | ||
"releaseDate": "2019-01-29", | ||
"eol": "2020-11-01", | ||
"latest": "3.9.6", | ||
"latestReleaseDate": "2020-04-23", | ||
"link": "https://alpinelinux.org/posts/Alpine-3.9.6-and-3.10.5-released.html", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.8", | ||
"releaseDate": "2018-06-26", | ||
"eol": "2020-05-01", | ||
"latest": "3.8.5", | ||
"latestReleaseDate": "2020-01-23", | ||
"link": "https://git.alpinelinux.org/aports/log/?h=3.8-stable", | ||
"lts": false | ||
}, | ||
{ | ||
"cycle": "3.7", | ||
"releaseDate": "2017-11-30", | ||
"eol": "2019-11-01", | ||
"latest": "3.7.3", | ||
"latestReleaseDate": "2019-03-06", | ||
"link": "https://git.alpinelinux.org/aports/log/?h=3.7-stable", | ||
"lts": false | ||
} | ||
] | ||
} | ||
} |