Skip to content

Commit

Permalink
Add build list view for containerId (#696)
Browse files Browse the repository at this point in the history


Signed-off-by: munishchouhan <[email protected]>
Signed-off-by: Paolo Di Tommaso <[email protected]>
Co-authored-by: Paolo Di Tommaso <[email protected]>
  • Loading branch information
munishchouhan and pditommaso authored Oct 16, 2024
1 parent fdf1be7 commit 3cabb83
Show file tree
Hide file tree
Showing 10 changed files with 379 additions and 43 deletions.
77 changes: 52 additions & 25 deletions src/main/groovy/io/seqera/wave/controller/ViewController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.QueryValue
import io.micronaut.scheduling.TaskExecutors
import io.micronaut.scheduling.annotation.ExecuteOn
import io.micronaut.views.ModelAndView
import io.micronaut.views.View
import io.seqera.wave.exception.HttpResponseException
import io.seqera.wave.exception.NotFoundException
Expand Down Expand Up @@ -114,55 +115,81 @@ class ViewController {
return binding
}

@View("build-view")
@Get('/builds/{buildId}')
HttpResponse viewBuild(String buildId) {
// check redirection for invalid suffix in the form `-nn`
final r1 = shouldRedirect1(buildId)
final r1 = isBuildInvalidSuffix(buildId)
if( r1 ) {
log.debug "Redirect to build page [1]: $r1"
return HttpResponse.redirect(URI.create(r1))
}
// check redirection when missing the suffix `_nn`
final r2 = shouldRedirect2(buildId)
if( r2 ) {
log.debug "Redirect to build page [2]: $r2"
return HttpResponse.redirect(URI.create(r2))

// check all builds matching the pattern
if( isBuildMissingSuffix(buildId) ) {
final builds = buildService.getAllBuilds(buildId)
if( !builds ) {
log.debug "Found not build with id: $buildId"
throw new NotFoundException("Unknown container build id '$buildId'")
}
if( builds.size()==1 ) {
log.debug "Redirect to build page [2]: ${builds.first().buildId}"
return HttpResponse.temporaryRedirect(URI.create("/view/builds/${builds.first().buildId}"))
}
else
return HttpResponse.ok(new ModelAndView("build-list", renderBuildsView(builds)))
}

// go ahead with proper handling
final record = buildService.getBuildRecord(buildId)
if( !record )
throw new NotFoundException("Unknown container build id '$buildId'")
return HttpResponse.ok(renderBuildView(record))
return HttpResponse.ok(new ModelAndView("build-view", renderBuildView(record)))
}

static final private Pattern DASH_SUFFIX = ~/([0-9a-zA-Z\-]+)-(\d+)$/
static final private Pattern DASH_SUFFIX_REGEX = ~/([0-9a-zA-Z\-]+)-(\d+)$/

static final private Pattern MISSING_SUFFIX = ~/([0-9a-zA-Z\-]+)(?<!_\d{2})$/
static final private Pattern CONTAINER_ID_REGEX = ~/((bd-)?[0-9a-z\-]{16})(?<!_\d{2})$/

protected String shouldRedirect1(String buildId) {
protected String isBuildInvalidSuffix(String buildId) {
// check for build id containing a -nn suffix
final check1 = DASH_SUFFIX.matcher(buildId)
final check1 = DASH_SUFFIX_REGEX.matcher(buildId)
if( check1.matches() ) {
return "/view/builds/${check1.group(1)}_${check1.group(2)}"
}
return null
}

protected String shouldRedirect2(String buildId) {
protected boolean isBuildMissingSuffix(String buildId) {
// check build id missing the _nn suffix
if( !MISSING_SUFFIX.matcher(buildId).matches() )
return null

final rec = buildService.getLatestBuild(buildId)
if( !rec )
return null
if( !rec.buildId.contains(buildId) )
return null
if( rec.buildId==buildId )
return null

return "/view/builds/${rec.buildId}"
return buildId
? CONTAINER_ID_REGEX.matcher(buildId).matches()
: false
}

Map<String,?> renderBuildsView(List<WaveBuildRecord> results) {
// create template binding
final binding = new ArrayList<Map<String,String>>()
for (def result : results){
final bind = new HashMap(20)
bind.build_id = result.buildId
bind.build_image = result.targetImage
bind.build_status = getStatus(result)
bind.build_time = formatTimestamp(result.startTime, result.offsetId) ?: '-'
binding.add(bind)
}
// result the main object
return Map.of("build_records", binding, 'server_url', serverUrl)
}

protected static String getStatus(WaveBuildRecord result){
if( result.done() && result.succeeded() )
return "SUCCEEDED"
else if ( result.done() && !result.succeeded() )
return "FAILED"
else if (!result.done())
return "IN PROGRESS"
else
return "UNKNOWN"
}

Map<String,String> renderBuildView(WaveBuildRecord result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,12 @@ interface ContainerBuildService {
*/
WaveBuildRecord getLatestBuild(String containerId)

/**
* Retrieve the all build record available for the specified container id.
*
* @param containerId The ID of the container for which the build record needs to be retrieve
* @return The {@link WaveBuildRecord} associated with the corresponding Id, or {@code null} if it cannot be found
*/
List<WaveBuildRecord> getAllBuilds(String containerId)

}
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,12 @@ class ContainerBuildServiceImpl implements ContainerBuildService, JobHandler<Bui
return persistenceService.latestBuild(containerId)
}

/**
* {@inheritDoc}
*/
@Override
List<WaveBuildRecord> getAllBuilds(String containerId) {
return persistenceService.allBuilds(containerId)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ interface PersistenceService {
*/
WaveBuildRecord latestBuild(String containerId)

/**
* Retrieve all {@link WaveBuildRecord} object for the given container id
*
* @param containerId The container id for which the latest build record should be retrieved
* @return The corresponding {@link WaveBuildRecord} object or {@code null} if no record is found
*/
List<WaveBuildRecord> allBuilds(String containerId)

/**
* Store a {@link WaveContainerRecord} object in the Surreal wave_request table.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ class LocalPersistenceService implements PersistenceService {
.reverse() [0]
}

@Override
List<WaveBuildRecord> allBuilds(String containerId) {
buildStore
.values()
.findAll( it-> it.buildId.contains(containerId) )
.sort { it.startTime }
.reverse()
}

@Override
WaveBuildRecord loadBuildSucceed(String targetImage, String digest) {
buildStore.values().find( (build) -> build.targetImage==targetImage && build.digest==digest && build.succeeded() )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,21 @@ class SurrealPersistenceService implements PersistenceService {
return result
}

@Override
List<WaveBuildRecord> allBuilds(String containerId) {
final query = """
select *
from wave_build
where buildId ~ '${containerId}'
order by startTime desc
""".stripIndent()
final json = surrealDb.sqlAsString(getAuthorization(), query)
final type = new TypeReference<ArrayList<SurrealResult<WaveBuildRecord>>>() {}
final data= json ? JacksonHelper.fromJson(json, type) : null
final result = data && data[0].result ? data[0].result : null
return result ? Arrays.asList(result) : null
}

@Override
void saveContainerRequest(WaveContainerRecord data) {
// note: use surreal sql in order to by-pass issue with large payload
Expand Down
100 changes: 100 additions & 0 deletions src/main/resources/io/seqera/wave/build-list.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Wave container build data">
<title>Wave container builds</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&display=swap');
pre {
white-space: pre-wrap;
overflow-x: auto;
overflow-y: auto;
background-color: #ededed;
padding: 15px;
border-radius: 4px;
margin-bottom: 30px;
max-height: 400px;
max-width: 100%;
word-wrap: break-word;
}
.table-container table{
width: 100%;
border-collapse: collapse;
}
.table-container th, .table-container td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #e5e7eb;
vertical-align: middle;
}
.table-container th {
font-weight: bold;
background-color: #f1f5f9;
}
.table-container td {
font-size: 14px;
}
.table-container a {
background-color: #f1f5f9;
text-decoration: none;
padding: 4px 8px;
border-radius: 4px;
display: inline-block;
}
.table-container a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div style="font-family:'-apple-system', BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; padding: 30px; max-width: 800px; margin: 0 auto;">
<div>
<img style="float:left; margin-top: 5px; margin-right: 10px; width: 30px;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD0AAAA8CAMAAADi4EJ+AAAApVBMVEUAAAA9lf4/nP88lf06lP49lf5Alv49lf09lf49lf09lf09lf49lf48lf49lf0/lP09lv89lf49lf09lf09lf09lv09lf49lf09lf09lv09lfw9lf49lf09lf09lf49lf09lv89lf49lv49lf09lf09lf09lf48lf89lf49lP09lP48lP09lP08lf89lf5Bn/8+l/8/mv9Anf9BoP9Cof9Fqv9Co/8+fXihAAAALnRSTlMA+wT9BvcPkOvAY7WBIsoURt6iiXA30LOoXT4c8ujbmTHjua+VeGsK1lQsKXpNcp061QAAA+NJREFUSMeVlolyozAMhhG200Lus7nva9susk267/9oS2BBMngys5pMhiB9/o2t+CdoRBhmX/3B97z3fmgFYS0ZtDZaJM8QWq187Op6XBtEY949dBeSkt606nAQTEdrtFoAgPbRA1PACZhZg923DWoAkZUIL91J4R9t205KBvJNo4B/g/vpyJZ0OnYzy2GaFDmia/Bndr/U/uXkJsYokbyiZTBGKLOqy1NnLuyng9VaV9I9vl6nB1TCtCWhK73DUgDwwpLzh3JZUHVt+dyuckkT+CqTYTBOlYMKg9ZSrxVF/Y0h6XaZk8EC+bQhQXPfxdOuO+2gZ6EqMYOCzr6/QAsmjDBfFikOywgJthFNiw2agLYfn3nHh077d+/Iisyykp6k/L5a5NUMldmvheIK6aiC++9a0JS2XYeV+fUyQs2n994v08/GJ3jWyu64sYojY4EvjP0dyKY0mAPBeXL6fe4ptIJ3oXqMaasnWKZEIvYFTM31Y60GEBxO56xkxlpgUsBEv6HKUa4csYq9Fs3GJzqFWgOnx5C2mq3ZczHC1zSIbK94UWSBS7+ihTL6wmG24oBXSnhoATrdLt1Gmmoh6LDw0RWLqiNrzRCjb+JEP+BJgjCpOneD+vAdqyAPZXeUc3dMGzTDTsbK+vDzH4N5mJ8LZXm3pEbN3gaSdFlcolP7o519PqJlIx8G8Wl8HayKkf4jQinZ6vJLCvbDLWCXNMrL4Dqt6aIzGt2a2dvo+JE/6kf71Hcy5eN9TY4bjfhQHh9b6dTiM+wD6nSY4YtI5Basko2HXmooQum7rM85jLdok/wPLbzuP6HuiurCgzuaDH3hwKeKxp37xLeRzdiXDtxf8z8Vh/fDlFgvLWniItF7PuwvQfbNaBcfmnLiZhvynjSahIluSpcTP1dJGcQoXLi5YyEz/0SYJZnoLyNEzYLtH+U6cHGK0fsWvcyABs6aVG/nnYV0FpwfU4Bx5WRyaxgMGg/jvWx0YYfb91BWiV2quH2/T251C85qzygEl5b09kjCiR21Sutk7CpCINj2HC+iWes4cIwwd2954fYthJ4GYbVZDE4GzfOr+z1EDdzKOpVAeDdkonrgHmD9/e9OpFIDgsF4ZB1qmfnHNQceP0xqdc5S0bBFdJtaAEcNB34oyFmCzZp5TldpeqXu032/AyfKrj+ZxBVJ+kr3/bRQOOyyIjoshDnc/A7M7fvY4vDtYEQpPfZ6KLFgdccxJHrZpxbw0wAGe1MXpv0SZiv9DlxYsMVh3HCzC52Rc58Dp0+DTgzqXuyx0XNFp9R+XFsjopiNp14bnZP2wqetZ+23xVfRV434C+G26LEOOFWjAAAAAElFTkSuQmCC">
<h1>Wave container build records</h1>
</div>
{{#if build_records}}
<div class="table-container">
<table cellpadding="4" >
<tr>
<th>Build ID</th>
<th>Container image</th>
<th>Build time</th>
<th>Status</th>
</tr>
{{#each build_records}}
<tr>
<td><a href="{{server_url}}/view/builds/{{build_id}}">{{build_id}}</td>
<td>{{build_image}}</td>
<td>{{build_time}}</td>
<td>{{build_status}}</td>
</tr>
{{/each}}
</table>
</div>
{{else}}
<pre> No Build Record Found</pre>
{{/if}}

<div class="footer" style="clear:both;width:100%;">
<hr class="footer-hr" style="height:0;overflow:visible;margin-top:30px;border:0;border-top:1px solid #eee;color:#999999;font-size:12px;line-height:18px;margin-bottom:30px;">
<img style="float:right; width: 150px;" src="data:image/png;base64,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">
<p class="footer-text" style="font-family:'-apple-system', BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';font-weight:normal;margin:0;margin-bottom:15px;color:#999999;font-size:12px;line-height:18px;">
<a href="{{server_url}}">{{server_url}}</a><br>
Seqera<br>
Carrer de Marià Aguiló, 28<br>
08005 Barcelona<br>
</p>
</div>

</div>
</body>
</html>
4 changes: 4 additions & 0 deletions src/main/resources/io/seqera/wave/build-view.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,11 @@

<tr>
<td>Container image</td>
{{#if build_in_progress}}
<td>{{build_image}}</td>
{{else}}
<td><a href="{{inspect_url}}">{{build_image}}</a></td>
{{/if}}
</tr>

<tr>
Expand Down
Loading

0 comments on commit 3cabb83

Please sign in to comment.