Skip to content

Commit

Permalink
Merge pull request #12 from slintes/ui
Browse files Browse the repository at this point in the history
Adding web UI
  • Loading branch information
slintes committed Aug 4, 2024
2 parents dc9053b + 20978eb commit 90158b7
Show file tree
Hide file tree
Showing 17 changed files with 2,444 additions and 272 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: stable
go-version-file: findIndexImage/go.mod
- name: Install binaryen
run: sudo apt-get install -y binaryen
# More assembly might be required: Docker logins, GPG, etc.
# It all depends on your needs.
- name: Run GoReleaser
Expand Down
3 changes: 2 additions & 1 deletion findIndexImage/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@

dist/
*.wasm
wasm_exec.js
14 changes: 7 additions & 7 deletions findIndexImage/.goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

version: 1
version: 2
project_name: findIndexImage

before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
# you may remove this if you don't need go generate
- go generate ./...
# build webassembly
- make wasm
# copy wasm js file
- make cp-wasm-js

builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
#- windows
#- darwin

archives:
- format: tar.gz
Expand Down
24 changes: 24 additions & 0 deletions findIndexImage/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
SHELL := /bin/bash

GOROOT := $(shell go env GOROOT)

.PHONY: build
build:
goreleaser release --snapshot --clean

.PHONY: lint
lint:
go mod tidy
go fmt
go vet
goreleaser check

.PHONY: cp-wasm-js
cp-wasm-js:
cp $(GOROOT)/misc/wasm/wasm_exec.js frontend/ui

.PHONY: wasm
wasm:
GOOS=js GOARCH=wasm go build -o frontend/ui/ui.wasm frontend/wasm/main.go
## install "binaryen" for this tool!
wasm-opt frontend/ui/ui.wasm --enable-bulk-memory -Oz -o frontend/ui/ui.wasm
9 changes: 9 additions & 0 deletions findIndexImage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Find Index Image
================

This a small tool for listing index images for RHWA operators.
This is using RH internal systems, so you need to be on the RH VPN
for getting results.

Further instructions can be found on https://spaces.redhat.com/display/dragonfly/How+to+get+latest+IIB

15 changes: 15 additions & 0 deletions findIndexImage/api/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package api

type ErrorNotFound struct{}

func (e ErrorNotFound) Error() string {
return "not found"
}

type ErrorServer struct {
Msg string
}

func (e ErrorServer) Error() string {
return e.Msg
}
116 changes: 116 additions & 0 deletions findIndexImage/api/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package api

import "time"

type Result struct {
Operator string `json:"operator"`
BundleImage string `json:"bundleImage"`
BundleRelease string `json:"bundleRelease"`
BundleVersion string `json:"bundleVersion"`
OcpVersion string `json:"ocpVersion"`
IndexImage string `json:"indexImage"`
IndexNumber string `json:"indexNumber"`
GeneratedAt time.Time `json:"generatedAt"`
}

type Messages struct {
Arguments struct {
Categories []interface{} `json:"categories"`
Contains []string `json:"contains"`
Delta float64 `json:"delta"`
End float64 `json:"end"`
Grouped bool `json:"grouped"`
Meta []interface{} `json:"meta"`
NotCategories []interface{} `json:"not_categories"`
NotPackages []interface{} `json:"not_packages"`
NotTopics []interface{} `json:"not_topics"`
NotUsers []interface{} `json:"not_users"`
Order string `json:"order"`
Packages []interface{} `json:"packages"`
Page int `json:"page"`
RowsPerPage int `json:"rows_per_page"`
Start float64 `json:"start"`
Topics []string `json:"topics"`
Users []interface{} `json:"users"`
} `json:"arguments"`
Count int `json:"count"`
Pages int `json:"pages"`
RawMessages []struct {
Certificate interface{} `json:"certificate"`
Crypto interface{} `json:"crypto"`
Headers struct {
CINAME string `json:"CI_NAME"`
CITYPE string `json:"CI_TYPE"`
JMSXUserID string `json:"JMSXUserID"`
Amq6100Destination string `json:"amq6100_destination"`
Amq6100OriginalDestination string `json:"amq6100_originalDestination"`
Category string `json:"category"`
CorrelationId string `json:"correlation-id"`
Destination string `json:"destination"`
Expires string `json:"expires"`
MessageId string `json:"message-id"`
OriginalDestination string `json:"original-destination"`
Persistent string `json:"persistent"`
Priority string `json:"priority"`
Source string `json:"source"`
Subscription string `json:"subscription"`
Timestamp string `json:"timestamp"`
Topic string `json:"topic"`
Type string `json:"type"`
Version string `json:"version"`
} `json:"headers"`
I int `json:"i"`
Msg struct {
Artifact struct {
AdvisoryId string `json:"advisory_id"`
BrewBuildTag string `json:"brew_build_tag"`
BrewBuildTarget string `json:"brew_build_target"`
Component string `json:"component"`
FullName string `json:"full_name"`
Id string `json:"id"`
ImageTag string `json:"image_tag"`
Issuer string `json:"issuer"`
Name string `json:"name"`
Namespace string `json:"namespace"`
Nvr string `json:"nvr"`
RegistryUrl string `json:"registry_url"`
Scratch string `json:"scratch"`
Type string `json:"type"`
} `json:"artifact"`
Ci struct {
Doc string `json:"doc"`
Email string `json:"email"`
Name string `json:"name"`
Team string `json:"team"`
Url string `json:"url"`
} `json:"ci"`
GeneratedAt time.Time `json:"generated_at"`
Index struct {
AddedBundleImages []string `json:"added_bundle_images"`
IndexImage string `json:"index_image"`
OcpVersion string `json:"ocp_version"`
} `json:"index"`
Pipeline struct {
Build string `json:"build"`
CpaasPipelineId string `json:"cpaas_pipeline_id"`
Id string `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
} `json:"pipeline"`
Run struct {
Log string `json:"log"`
Url string `json:"url"`
} `json:"run"`
Timestamp time.Time `json:"timestamp"`
Version string `json:"version"`
} `json:"msg"`
MsgId string `json:"msg_id"`
Signature interface{} `json:"signature"`
SourceName string `json:"source_name"`
SourceVersion string `json:"source_version"`
Timestamp float64 `json:"timestamp"`
Topic string `json:"topic"`
Username interface{} `json:"username"`
} `json:"raw_messages"`
Total int `json:"total"`
}
172 changes: 172 additions & 0 deletions findIndexImage/backend/datagrepper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package backend

import (
"crypto/tls"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"sort"
"strings"
"sync"
"time"

"golang.org/x/mod/semver"

"github.com/medik8s/findIndexImage/api"
)

var (
cachedResults []api.Result
lock sync.Mutex
)

func GetIndexImages(reload bool) ([]api.Result, error) {

lock.Lock()
defer lock.Unlock()

if cachedResults != nil &&
len(cachedResults) > 0 &&
!reload {

log.Println("using cached results")
return cachedResults, nil
}

topic := "/topic/VirtualTopic.eng.ci.redhat-container-image.index.built"
searchTerm := "workload-availability"
timeFrame := 4 * 7 * 24 * time.Hour // 4 weeks
rowsPerPage := 100 // 100 is max allowed value!

// keys are ocp version, operator name and operator version
var results []api.Result

tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}

for page := 1; true; page++ {
url := fmt.Sprintf("https://datagrepper.engineering.redhat.com/raw?topic=%s&delta=%v&contains=%s&rows_per_page=%v&page=%v", topic, int(timeFrame.Seconds()), searchTerm, rowsPerPage, page)
//fmt.Printf("URL: %s\n\n", url)
//fmt.Println("getting more results, please wait...")

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}

resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

responseBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

if resp.StatusCode != 200 {
fmt.Printf("HTTP status code: %d\n", resp.StatusCode)
fmt.Printf("Response: %s\n", string(responseBytes))
return nil, api.ErrorServer{Msg: fmt.Sprintf("server error: http status code: %v, response %v", resp.StatusCode, string(responseBytes))}
}

messages := &api.Messages{}
err = json.Unmarshal(responseBytes, messages)
if err != nil {
return nil, err
}

if len(messages.RawMessages) == 0 {
return nil, api.ErrorNotFound{}
}

rawMessages := messages.RawMessages
// sort by release and not build date, in case a newer build was finished faster than an older one
sort.Slice(rawMessages, func(i, j int) bool {
getIdentifier := func(nvr string) string {
operator, version, release := getOperatorVersionReleaseFromNvr(nvr)
return fmt.Sprintf("%s-%s-%s", operator, version, release)
}
return getIdentifier(rawMessages[i].Msg.Artifact.Nvr) < getIdentifier(rawMessages[j].Msg.Artifact.Nvr)
})

for i := 0; i < len(rawMessages); i++ {
message := messages.RawMessages[i].Msg
ocpVersion := message.Index.OcpVersion
nvr := message.Artifact.Nvr
operator, version, release := getOperatorVersionReleaseFromNvr(nvr)
// return latest build only
for _, result := range results {
if result.OcpVersion == ocpVersion &&
result.Operator == operator &&
result.BundleVersion == version {
continue
}
}
generatedAt := message.GeneratedAt
bundleImage := message.Index.AddedBundleImages[0]
indexImage := message.Index.IndexImage
indexNr := getNrFromIndexImage(indexImage)
result := api.Result{
Operator: operator,
BundleVersion: version,
BundleRelease: release,
BundleImage: bundleImage,
OcpVersion: ocpVersion,
IndexImage: indexImage,
IndexNumber: indexNr,
GeneratedAt: generatedAt,
}
results = append(results, result)
}

if messages.Pages == page {
break
}

}

// sort by OCP version, operator, release
sort.Slice(results, func(i, j int) bool {
a := results[i]
b := results[j]
if a.OcpVersion != b.OcpVersion {
return a.OcpVersion > b.OcpVersion
}
if a.Operator != b.Operator {
return a.Operator < b.Operator
}
return semver.Compare(a.BundleRelease, b.BundleRelease) == 1
})

cachedResults = results
return results, nil
}

func getOperatorVersionReleaseFromNvr(nvr string) (string, string, string) {
match := "-bundle-container-"
index := strings.Index(nvr, match)
if index == -1 {
fmt.Printf("could not find operator and version in NVR: %s\n", nvr)
return nvr, "n/a", "n/a"
}
operator := nvr[:index]
release := nvr[index+len(match):]
version := strings.Split(release, "-")[0]
return operator, version, release
}

func getNrFromIndexImage(indexImage string) string {
match := "/iib:"
index := strings.Index(indexImage, match)
if index == -1 {
fmt.Printf("could not find index number in index image: %s\n", indexImage)
return indexImage
}
return indexImage[index+len(match):]
}
Loading

0 comments on commit 90158b7

Please sign in to comment.