Skip to content

Commit

Permalink
Includes quay_overflow.go file and quay_overflow_test.go
Browse files Browse the repository at this point in the history
Signed-off-by: dlaw4608 <[email protected]>
  • Loading branch information
dlaw4608 authored and dlaw4608 committed Aug 30, 2024
1 parent ecdb180 commit d66c920
Show file tree
Hide file tree
Showing 6 changed files with 439 additions and 68 deletions.
116 changes: 50 additions & 66 deletions .github/workflows/build-images-base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,31 +85,26 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Install qemu dependency
run: |
sudo apt-get update
sudo apt-get install -y qemu-user-static
- name: Build Image
id: build-image
uses: redhat-actions/buildah-build@v2
with:
image: ${{ env.OPERATOR_NAME }}
tags: ${{ env.IMG_TAGS }}
platforms: linux/amd64,linux/arm64
dockerfiles: |
./Dockerfile
- name: Push Image
if: ${{ !env.ACT }}
id: push-to-quay
uses: redhat-actions/push-to-registry@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to container registry
uses: docker/login-action@v2
with:
image: ${{ steps.build-image.outputs.image }}
tags: ${{ steps.build-image.outputs.tags }}
registry: ${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}
username: ${{ secrets.IMG_REGISTRY_USERNAME }}
password: ${{ secrets.IMG_REGISTRY_TOKEN }}
registry: ${{ env.IMG_REGISTRY_HOST }}
- name: Build and Push Image
id: build-image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}/${{ env.OPERATOR_NAME }}:${{ env.IMG_TAGS }}
- name: Print Image URL
run: echo "Image pushed to ${{ steps.push-to-quay.outputs.registry-paths }}"
run: echo "Image pushed to ${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}/${{ env.OPERATOR_NAME }}:${{ env.IMG_TAGS }}"

build-bundle:
needs: build
Expand All @@ -123,10 +118,6 @@ jobs:
id: go
- name: Check out code
uses: actions/checkout@v3
- name: Install qemu dependency
run: |
sudo apt-get update
sudo apt-get install -y qemu-user-static
- name: Run make bundle
id: make-bundle
run: |
Expand All @@ -138,27 +129,26 @@ jobs:
WASM_SHIM_VERSION=${{ inputs.wasmShimVersion }} \
REPLACES_VERSION=${{ inputs.replacesVersion }} \
CHANNELS=${{ inputs.channels }}
- name: Build Image
id: build-image
uses: redhat-actions/buildah-build@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to container registry
uses: docker/login-action@v2
with:
image: ${{ env.OPERATOR_NAME }}-bundle
tags: ${{ env.IMG_TAGS }}
platforms: linux/amd64,linux/arm64
dockerfiles: |
./bundle.Dockerfile
- name: Push Image
if: ${{ !env.ACT }}
id: push-to-quay
uses: redhat-actions/push-to-registry@v2
with:
image: ${{ steps.build-image.outputs.image }}
tags: ${{ steps.build-image.outputs.tags }}
registry: ${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}
username: ${{ secrets.IMG_REGISTRY_USERNAME }}
password: ${{ secrets.IMG_REGISTRY_TOKEN }}
- name: Print Image URL
run: echo "Image pushed to ${{ steps.push-to-quay.outputs.registry-paths }}"
registry: ${{ env.IMG_REGISTRY_HOST }}
- name: Build and Push Bundle Image
id: build-bundle-image
uses: docker/build-push-action@v5
with:
context: .
file: ./bundle.Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}/${{ env.OPERATOR_NAME }}-bundle:${{ env.IMG_TAGS }}
- name: Print Bundle Image URL
run: echo "Bundle image pushed to ${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}/${{ env.OPERATOR_NAME }}-bundle:${{ env.IMG_TAGS }}"

build-catalog:
name: Build Catalog
Expand All @@ -182,29 +172,23 @@ jobs:
WASM_SHIM_VERSION=${{ inputs.wasmShimVersion }} \
REPLACES_VERSION=${{ inputs.replacesVersion }} \
CHANNELS=${{ inputs.channels }}
- name: Install qemu dependency
run: |
sudo apt-get update
sudo apt-get install -y qemu-user-static
- name: Build Image
id: build-image
uses: redhat-actions/buildah-build@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to container registry
uses: docker/login-action@v2
with:
image: ${{ env.OPERATOR_NAME }}-catalog
tags: ${{ env.IMG_TAGS }}
platforms: linux/amd64,linux/arm64
context: ./catalog
dockerfiles: |
./catalog/kuadrant-operator-catalog.Dockerfile
- name: Push Image
if: ${{ !env.ACT }}
id: push-to-quay
uses: redhat-actions/push-to-registry@v2
with:
image: ${{ steps.build-image.outputs.image }}
tags: ${{ steps.build-image.outputs.tags }}
registry: ${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}
username: ${{ secrets.IMG_REGISTRY_USERNAME }}
password: ${{ secrets.IMG_REGISTRY_TOKEN }}
- name: Print Image URL
run: echo "Image pushed to ${{ steps.push-to-quay.outputs.registry-paths }}"
registry: ${{ env.IMG_REGISTRY_HOST }}
- name: Build and Push Catalog Image
id: build-catalog-image
uses: docker/build-push-action@v5
with:
context: ./catalog
file: ./catalog/kuadrant-operator-catalog.Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}/${{ env.OPERATOR_NAME }}-catalog:${{ env.IMG_TAGS }}
- name: Print Catalog Image URL
run: echo "Catalog image pushed to ${{ env.IMG_REGISTRY_HOST }}/${{ env.IMG_REGISTRY_ORG }}/${{ env.OPERATOR_NAME }}-catalog:${{ env.IMG_TAGS }}"
7 changes: 5 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the manager binary
FROM golang:1.22 as builder
FROM --platform=$BUILDPLATFORM golang:1.22 as builder

WORKDIR /workspace
# Copy the Go Modules manifests
Expand All @@ -15,8 +15,11 @@ COPY api/ api/
COPY controllers/ controllers/
COPY pkg/ pkg/

# Set environment variables for cross-compilation
ARG TARGETARCH

# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -a -o manager main.go

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ require (
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
Expand All @@ -134,9 +135,12 @@ require (
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/tidwall/gjson v1.14.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/vitorsalgado/mocha/v3 v3.0.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
Expand All @@ -413,6 +415,8 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/vitorsalgado/mocha/v3 v3.0.2 h1:uTx/+7kZvTWddXzoF34vUQTa3OL9OE+f5fPjD2XCMoY=
github.com/vitorsalgado/mocha/v3 v3.0.2/go.mod h1:ZMpyjuNfWPqLP2v7ztaaLJwOcyl4jmmHVQCEoDsFD0Q=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
Expand Down
168 changes: 168 additions & 0 deletions quay/quay_overflow.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package main

import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"

"k8s.io/client-go/rest"
)

const (
repo = "kuadrant/kuadrant-operator"
baseURL = "https://quay.io/api/v1/repository/"
)

var (
robotPass = os.Getenv("ROBOT_PASS")
robotUser = os.Getenv("ROBOT_USER")
accessToken = os.Getenv("ACCESS_TOKEN")
preserveSubstring = "latest" // Example Tag name that wont be deleted i.e relevant tags
)

// Tag represents a tag in the repository.
type Tag struct {
Name string `json:"name"`
LastModified string `json:"last_modified"`
}

// TagsResponse represents the structure of the API response that contains tags.
type TagsResponse struct {
Tags []Tag `json:"tags"`
}

func main() {
client := &http.Client{}

// Fetch tags from the API
tags, err := fetchTags(client)
if err != nil {
fmt.Println("Error fetching tags:", err)
return
}

// Use filterTags to get tags to delete and remaining tags
tagsToDelete, remainingTags := filterTags(tags, preserveSubstring)

// Delete tags and update remainingTags
for tagName := range tagsToDelete {
if deleteTag(client, accessToken, tagName) {
delete(remainingTags, tagName) // Remove deleted tag from remainingTags
}
}

// Print remaining tags
fmt.Println("Remaining tags:")
for tag := range remainingTags {
fmt.Println(tag)
}
}

// fetchTags retrieves the tags from the repository using the Quay.io API.
func fetchTags(client rest.HTTPClient) ([]Tag, error) {
// TODO - DO you want to seperate out builidng the request to a function to unit test?
// TODO - Is adding the headers even needed to fetch tags for a public repo?
req, err := http.NewRequest("GET", baseURL+repo+"/tag", nil)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}

// Prioritize Bearer token for authorization
if accessToken != "" {
req.Header.Add("Authorization", "Bearer "+accessToken)
} else {
// Fallback to Basic Authentication if no access token
auth := base64.StdEncoding.EncodeToString([]byte(robotUser + ":" + robotPass))
req.Header.Add("Authorization", "Basic "+auth)
}

// Execute the request
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making request: %w", err)
}
defer resp.Body.Close()

// Handle possible non-200 status codes
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("error: received status code %d\nBody: %s", resp.StatusCode, string(body))
}

// Read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

// Parse the JSON response
var tagsResp TagsResponse
if err := json.Unmarshal(body, &tagsResp); err != nil {
return nil, fmt.Errorf("error unmarshalling response: %w", err)
}

return tagsResp.Tags, nil
}

// filterTags takes a slice of tags and returns two maps: one for tags to delete and one for remaining tags.
func filterTags(tags []Tag, preserveSubstring string) (map[string]struct{}, map[string]struct{}) {
// Calculate the cutoff time
cutOffTime := time.Now().AddDate(0, 0, 0).Add(0 * time.Hour).Add(-1 * time.Minute)

tagsToDelete := make(map[string]struct{})
remainingTags := make(map[string]struct{})

for _, tag := range tags {
// Parse the LastModified timestamp
lastModified, err := time.Parse(time.RFC1123, tag.LastModified)
if err != nil {
fmt.Println("Error parsing time:", err)
continue
}

// Check if tag should be deleted
if lastModified.Before(cutOffTime) && !containsSubstring(tag.Name, preserveSubstring) {
tagsToDelete[tag.Name] = struct{}{}
} else {
remainingTags[tag.Name] = struct{}{}
}
}

return tagsToDelete, remainingTags
}

func containsSubstring(tagName, substring string) bool {
return strings.Contains(tagName, substring)
}

// deleteTag sends a DELETE request to remove the specified tag from the repository
// Returns true if successful, false otherwise
func deleteTag(client rest.HTTPClient, accessToken, tagName string) bool {
req, err := http.NewRequest("DELETE", baseURL+repo+"/tag/"+tagName, nil)
if err != nil {
fmt.Println("Error creating DELETE request:", err)
return false
}
req.Header.Add("Authorization", "Bearer "+accessToken)

resp, err := client.Do(req)
if err != nil {
fmt.Println("Error deleting tag:", err)
return false
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusNoContent {
fmt.Printf("Successfully deleted tag: %s\n", tagName)
return true
} else {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Failed to delete tag %s: Status code %d\nBody: %s\n", tagName, resp.StatusCode, string(body))
return false
}
}
Loading

0 comments on commit d66c920

Please sign in to comment.