Skip to content

Commit e6d4980

Browse files
committed
Includes quay_overflow.go file and quay_overflow_test.go
Signed-off-by: dlaw4608 <[email protected]>
1 parent ddf59ad commit e6d4980

File tree

3 files changed

+217
-24
lines changed

3 files changed

+217
-24
lines changed

quay/quay_overflow.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ import (
99
"os"
1010
"strings"
1111
"time"
12+
13+
"k8s.io/client-go/rest"
1214
)
1315

1416
const (
15-
repo = "test_org_123/kuadrant-operator"
17+
repo = "kuadrant/kuadrant-operator"
1618
baseURL = "https://quay.io/api/v1/repository/"
1719
)
1820

1921
var (
2022
robotPass = os.Getenv("ROBOT_PASS")
2123
robotUser = os.Getenv("ROBOT_USER")
2224
accessToken = os.Getenv("ACCESS_TOKEN")
23-
preserveSubstring = "danlaw345" // Example Tag name that wont be deleted i.e relevant tags
25+
preserveSubstring = "latest" // Example Tag name that wont be deleted i.e relevant tags
2426
)
2527

2628
// Tag represents a tag in the repository.
@@ -62,7 +64,9 @@ func main() {
6264
}
6365

6466
// fetchTags retrieves the tags from the repository using the Quay.io API.
65-
func fetchTags(client *http.Client) ([]Tag, error) {
67+
func fetchTags(client rest.HTTPClient) ([]Tag, error) {
68+
// TODO - DO you want to seperate out builidng the request to a function to unit test?
69+
// TODO - Is adding the headers even needed to fetch tags for a public repo?
6670
req, err := http.NewRequest("GET", baseURL+repo+"/tag", nil)
6771
if err != nil {
6872
return nil, fmt.Errorf("error creating request: %w", err)
@@ -138,7 +142,7 @@ func containsSubstring(tagName, substring string) bool {
138142

139143
// deleteTag sends a DELETE request to remove the specified tag from the repository
140144
// Returns true if successful, false otherwise
141-
func deleteTag(client *http.Client, accessToken, tagName string) bool {
145+
func deleteTag(client rest.HTTPClient, accessToken, tagName string) bool {
142146
req, err := http.NewRequest("DELETE", baseURL+repo+"/tag/"+tagName, nil)
143147
if err != nil {
144148
fmt.Println("Error creating DELETE request:", err)
@@ -161,4 +165,4 @@ func deleteTag(client *http.Client, accessToken, tagName string) bool {
161165
fmt.Printf("Failed to delete tag %s: Status code %d\nBody: %s\n", tagName, resp.StatusCode, string(body))
162166
return false
163167
}
164-
}
168+
}

quay/quay_overflow_test.go

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"io"
7+
"net/http"
8+
"strings"
9+
"testing"
10+
"time"
11+
12+
"k8s.io/client-go/rest"
13+
)
14+
15+
type MockHTTPClient struct {
16+
wantErr bool
17+
mutateFn func(res *http.Response)
18+
}
19+
20+
func (m MockHTTPClient) Do(request *http.Request) (*http.Response, error) {
21+
if m.wantErr {
22+
return nil, errors.New("oops")
23+
}
24+
25+
resp := &http.Response{}
26+
if m.mutateFn != nil {
27+
m.mutateFn(resp)
28+
}
29+
30+
return resp, nil
31+
}
32+
33+
var _ rest.HTTPClient = &MockHTTPClient{}
34+
35+
func Test_fetchTags(t *testing.T) {
36+
t.Run("test error making request", func(t *testing.T) {
37+
tags, err := fetchTags(&MockHTTPClient{wantErr: true})
38+
39+
if err == nil {
40+
t.Error("error expected")
41+
}
42+
43+
if err.Error() != "error making request: oops" {
44+
t.Errorf("error expected, got %s", err.Error())
45+
}
46+
47+
if tags != nil {
48+
t.Error("expected nil tags")
49+
}
50+
})
51+
52+
t.Run("test error for non-200 status codes", func(t *testing.T) {
53+
tags, err := fetchTags(&MockHTTPClient{mutateFn: func(res *http.Response) {
54+
res.Status = string(rune(400))
55+
res.Body = io.NopCloser(bytes.NewReader(nil))
56+
}})
57+
58+
if err == nil {
59+
t.Error("error expected")
60+
}
61+
62+
if strings.Contains(err.Error(), "tags, error: received status code 400") {
63+
t.Errorf("error expected, got %s", err.Error())
64+
}
65+
66+
if tags != nil {
67+
t.Error("expected nil tags")
68+
}
69+
})
70+
71+
t.Run("test error parsing json", func(t *testing.T) {
72+
tags, err := fetchTags(&MockHTTPClient{mutateFn: func(res *http.Response) {
73+
res.Status = string(rune(200))
74+
res.Body = io.NopCloser(bytes.NewReader([]byte("{notTags: error}")))
75+
}})
76+
77+
if err == nil {
78+
t.Error("error expected")
79+
}
80+
81+
if strings.Contains(err.Error(), "error unmarshalling response:") {
82+
t.Errorf("error expected, got %s", err.Error())
83+
}
84+
85+
if tags != nil {
86+
t.Error("expected nil tags")
87+
}
88+
})
89+
90+
t.Run("test successful response with tags", func(t *testing.T) {
91+
mockJSONResponse := `{
92+
"tags": [
93+
{"name": "v1.0.0", "last_modified": "Mon, 02 Jan 2006 15:04:05 MST"},
94+
{"name": "v1.1.0", "last_modified": "Tue, 03 Jan 2006 15:04:05 MST"},
95+
{"name": "latest", "last_modified": "Wed, 04 Jan 2006 15:04:05 MST"}
96+
]
97+
}`
98+
99+
tags, err := fetchTags(&MockHTTPClient{mutateFn: func(res *http.Response) {
100+
res.StatusCode = http.StatusOK
101+
res.Body = io.NopCloser(bytes.NewReader([]byte(mockJSONResponse)))
102+
}})
103+
104+
if err != nil {
105+
t.Fatalf("unexpected error: %v", err)
106+
}
107+
108+
// Validate the returned tags
109+
if len(tags) != 3 {
110+
t.Fatalf("expected 3 tags, got %d", len(tags))
111+
}
112+
113+
expectedTags := map[string]string{
114+
"v1.0.0": "Mon, 02 Jan 2006 15:04:05 MST",
115+
"v1.1.0": "Tue, 03 Jan 2006 15:04:05 MST",
116+
"latest": "Wed, 04 Jan 2006 15:04:05 MST",
117+
}
118+
119+
for _, tag := range tags {
120+
if expectedDate, ok := expectedTags[tag.Name]; !ok || expectedDate != tag.LastModified {
121+
t.Errorf("unexpected tag: got %v, expected %v", tag, expectedTags[tag.Name])
122+
}
123+
}
124+
})
125+
}
126+
127+
func Test_deleteTag(t *testing.T) {
128+
t.Run("test successful delete", func(t *testing.T) {
129+
client := &MockHTTPClient{mutateFn: func(res *http.Response) {
130+
res.StatusCode = http.StatusNoContent
131+
res.Body = io.NopCloser(bytes.NewReader(nil))
132+
}}
133+
134+
success := deleteTag(client, "fake_access_token", "v1.0.0")
135+
136+
if !success {
137+
t.Error("expected successful delete, got failure")
138+
}
139+
})
140+
141+
t.Run("test delete with error response", func(t *testing.T) {
142+
client := &MockHTTPClient{mutateFn: func(res *http.Response) {
143+
res.StatusCode = http.StatusInternalServerError
144+
res.Body = io.NopCloser(bytes.NewReader([]byte("internal server error")))
145+
}}
146+
147+
success := deleteTag(client, "fake_access_token", "v1.0.0")
148+
149+
if success {
150+
t.Error("expected failure, got success")
151+
}
152+
})
153+
154+
t.Run("test error making delete request", func(t *testing.T) {
155+
client := &MockHTTPClient{wantErr: true}
156+
157+
success := deleteTag(client, "fake_access_token", "v1.0.0")
158+
159+
if success {
160+
t.Error("expected failure, got success")
161+
}
162+
})
163+
}
164+
165+
func Test_filterTags(t *testing.T) {
166+
t.Run("test filter tags correctly", func(t *testing.T) {
167+
tags := []Tag{
168+
{"nightly-build", time.Now().Add(-24 * time.Hour).Format(time.RFC1123)}, // Old tag, should be deleted
169+
{"v1.1.0", time.Now().Format(time.RFC1123)}, // Recent tag, should be kept
170+
{"latest", time.Now().Add(-24 * time.Hour).Format(time.RFC1123)}, // Old tag, but name contains preserveSubstring
171+
}
172+
173+
preserveSubstring := "latest"
174+
175+
tagsToDelete, remainingTags := filterTags(tags, preserveSubstring)
176+
177+
if len(tagsToDelete) != 1 || len(remainingTags) != 2 {
178+
t.Fatalf("expected 1 tag to delete and 2 remaining, got %d to delete and %d remaining", len(tagsToDelete), len(remainingTags))
179+
}
180+
181+
if _, ok := tagsToDelete["nightly-build"]; !ok {
182+
t.Error("expected nightly-build to be deleted")
183+
}
184+
185+
if _, ok := remainingTags["v1.1.0"]; !ok {
186+
t.Error("expected v1.1.0 to be kept")
187+
}
188+
189+
if _, ok := remainingTags["latest"]; !ok {
190+
t.Error("expected latest to be kept")
191+
}
192+
})
193+
194+
t.Run("test filter tags with no deletions", func(t *testing.T) {
195+
tags := []Tag{
196+
{"v1.1.0", time.Now().Format(time.RFC1123)}, // Recent tag, should be kept
197+
{"latest", time.Now().Format(time.RFC1123)}, // Recent tag, should be kept
198+
}
199+
200+
preserveSubstring := "latest"
201+
202+
tagsToDelete, remainingTags := filterTags(tags, preserveSubstring)
203+
204+
if len(tagsToDelete) != 0 || len(remainingTags) != 2 {
205+
t.Fatalf("expected 0 tags to delete and 2 remaining, got %d to delete and %d remaining", len(tagsToDelete), len(remainingTags))
206+
}
207+
})
208+
}

quay/test/mirror_repo.sh

-19
This file was deleted.

0 commit comments

Comments
 (0)