mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-26 05:19:05 +01:00
commit
d49e5cda64
@ -482,12 +482,12 @@ func main() {
|
|||||||
if cfg.Retention.IncludeDB {
|
if cfg.Retention.IncludeDB {
|
||||||
cnt, err := jobRepo.DeleteJobsBefore(startTime)
|
cnt, err := jobRepo.DeleteJobsBefore(startTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error while deleting retention jobs from db: %s", err.Error())
|
log.Errorf("Error while deleting retention jobs from db: %v", err)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Retention: Removed %d jobs from db", cnt)
|
log.Infof("Retention: Removed %d jobs from db", cnt)
|
||||||
}
|
}
|
||||||
if err = jobRepo.Optimize(); err != nil {
|
if err = jobRepo.Optimize(); err != nil {
|
||||||
log.Errorf("Error occured in db optimization: %s", err.Error())
|
log.Errorf("Error occured in db optimization: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -497,12 +497,21 @@ func main() {
|
|||||||
log.Info("Register compression service")
|
log.Info("Register compression service")
|
||||||
|
|
||||||
s.Every(1).Day().At("5:00").Do(func() {
|
s.Every(1).Day().At("5:00").Do(func() {
|
||||||
|
var jobs []*schema.Job
|
||||||
|
|
||||||
ar := archive.GetHandle()
|
ar := archive.GetHandle()
|
||||||
startTime := time.Now().Unix() - int64(cfg.Compression*24*3600)
|
startTime := time.Now().Unix() - int64(cfg.Compression*24*3600)
|
||||||
lastTime := ar.CompressLast(startTime)
|
lastTime := ar.CompressLast(startTime)
|
||||||
jobs, err := jobRepo.FindJobsBetween(lastTime, startTime)
|
if startTime == lastTime {
|
||||||
|
log.Info("Compression Service - Complete archive run")
|
||||||
|
jobs, err = jobRepo.FindJobsBetween(0, startTime)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
jobs, err = jobRepo.FindJobsBetween(lastTime, startTime)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Error while looking for retention jobs: %s", err.Error())
|
log.Warnf("Error while looking for retention jobs: %v", err)
|
||||||
}
|
}
|
||||||
ar.Compress(jobs)
|
ar.Compress(jobs)
|
||||||
})
|
})
|
||||||
|
@ -204,7 +204,10 @@ func (r *JobRepository) CountJobs(
|
|||||||
|
|
||||||
func SecurityCheck(ctx context.Context, query sq.SelectBuilder) (queryOut sq.SelectBuilder, err error) {
|
func SecurityCheck(ctx context.Context, query sq.SelectBuilder) (queryOut sq.SelectBuilder, err error) {
|
||||||
user := auth.GetUser(ctx)
|
user := auth.GetUser(ctx)
|
||||||
if user == nil || user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleApi}) { // Admin & Co. : All jobs
|
if user == nil {
|
||||||
|
var qnil sq.SelectBuilder
|
||||||
|
return qnil, fmt.Errorf("user context is nil!")
|
||||||
|
} else if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport}) { // Admin & Co. : All jobs
|
||||||
return query, nil
|
return query, nil
|
||||||
} else if user.HasRole(auth.RoleManager) { // Manager : Add filter for managed projects' jobs only + personal jobs
|
} else if user.HasRole(auth.RoleManager) { // Manager : Add filter for managed projects' jobs only + personal jobs
|
||||||
if len(user.Projects) != 0 {
|
if len(user.Projects) != 0 {
|
||||||
@ -215,9 +218,12 @@ func SecurityCheck(ctx context.Context, query sq.SelectBuilder) (queryOut sq.Sel
|
|||||||
}
|
}
|
||||||
} else if user.HasRole(auth.RoleUser) { // User : Only personal jobs
|
} else if user.HasRole(auth.RoleUser) { // User : Only personal jobs
|
||||||
return query.Where("job.user = ?", user.Username), nil
|
return query.Where("job.user = ?", user.Username), nil
|
||||||
} else { // Unauthorized : Error
|
} else {
|
||||||
var qnil sq.SelectBuilder
|
// Shortterm compatibility: Return User-Query if no roles:
|
||||||
return qnil, fmt.Errorf("user '%s' with unknown roles [%#v]", user.Username, user.Roles)
|
return query.Where("job.user = ?", user.Username), nil
|
||||||
|
// // On the longterm: Return Error instead of fallback:
|
||||||
|
// var qnil sq.SelectBuilder
|
||||||
|
// return qnil, fmt.Errorf("user '%s' with unknown roles [%#v]", user.Username, user.Roles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,12 +88,15 @@ func (r *JobRepository) CountTags(user *auth.User) (tags []schema.Tag, counts ma
|
|||||||
LeftJoin("jobtag jt ON t.id = jt.tag_id").
|
LeftJoin("jobtag jt ON t.id = jt.tag_id").
|
||||||
GroupBy("t.tag_name")
|
GroupBy("t.tag_name")
|
||||||
|
|
||||||
if user != nil && user.HasRole(auth.RoleUser) { // USER: Only count own jobs
|
if user != nil && user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport}) { // ADMIN || SUPPORT: Count all jobs
|
||||||
q = q.Where("jt.job_id IN (SELECT id FROM job WHERE job.user = ?)", user.Username)
|
log.Info("CountTags: User Admin or Support -> Count all Jobs for Tags")
|
||||||
|
// Unchanged: Needs to be own case still, due to UserRole/NoRole compatibility handling in else case
|
||||||
} else if user != nil && user.HasRole(auth.RoleManager) { // MANAGER: Count own jobs plus project's jobs
|
} else if user != nil && user.HasRole(auth.RoleManager) { // MANAGER: Count own jobs plus project's jobs
|
||||||
// Build ("project1", "project2", ...) list of variable length directly in SQL string
|
// Build ("project1", "project2", ...) list of variable length directly in SQL string
|
||||||
q = q.Where("jt.job_id IN (SELECT id FROM job WHERE job.user = ? OR job.project IN (\""+strings.Join(user.Projects, "\",\"")+"\"))", user.Username)
|
q = q.Where("jt.job_id IN (SELECT id FROM job WHERE job.user = ? OR job.project IN (\""+strings.Join(user.Projects, "\",\"")+"\"))", user.Username)
|
||||||
} // else: ADMIN || SUPPORT: Count all jobs
|
} else if user != nil { // USER OR NO ROLE (Compatibility): Only count own jobs
|
||||||
|
q = q.Where("jt.job_id IN (SELECT id FROM job WHERE job.user = ?)", user.Username)
|
||||||
|
}
|
||||||
|
|
||||||
rows, err := q.RunWith(r.stmtCache).Query()
|
rows, err := q.RunWith(r.stmtCache).Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -65,12 +65,10 @@
|
|||||||
{#each managerviews as item}
|
{#each managerviews as item}
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
||||||
{/each}
|
{/each}
|
||||||
{:else if authlevel == roles.user}
|
{:else} <!-- Compatibility: Handle "user role" or "no role" as identical-->
|
||||||
{#each userviews as item}
|
{#each userviews as item}
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
|
||||||
<p>API User or Unauthorized!</p>
|
|
||||||
{/if}
|
{/if}
|
||||||
{#each viewsPerCluster.filter(item => item.requiredRole <= authlevel) as item}
|
{#each viewsPerCluster.filter(item => item.requiredRole <= authlevel) as item}
|
||||||
<NavItem>
|
<NavItem>
|
||||||
|
@ -161,7 +161,7 @@
|
|||||||
<tr class="mb-2"><th>User Name</th><th>Number of Nodes</th></tr>
|
<tr class="mb-2"><th>User Name</th><th>Number of Nodes</th></tr>
|
||||||
{#each $mainQuery.data.topUsers.sort((a, b) => b.count - a.count) as { name, count }}
|
{#each $mainQuery.data.topUsers.sort((a, b) => b.count - a.count) as { name, count }}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col"><a href="/monitoring/user/{name}">{name}</a></th>
|
<th scope="col"><a href="/monitoring/user/{name}?cluster={cluster}&state=running">{name}</a></th>
|
||||||
<td>{count}</td>
|
<td>{count}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
@ -182,7 +182,7 @@
|
|||||||
<tr class="mb-2"><th>Project Code</th><th>Number of Nodes</th></tr>
|
<tr class="mb-2"><th>Project Code</th><th>Number of Nodes</th></tr>
|
||||||
{#each $mainQuery.data.topProjects.sort((a, b) => b.count - a.count) as { name, count }}
|
{#each $mainQuery.data.topProjects.sort((a, b) => b.count - a.count) as { name, count }}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col"><a href="/monitoring/jobs/?project={name}&projectMatch=eq">{name}</a></th>
|
<th scope="col"><a href="/monitoring/jobs/?cluster={cluster}&state=running&project={name}&projectMatch=eq">{name}</a></th>
|
||||||
<td>{count}</td>
|
<td>{count}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -23,19 +23,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let timeoutId = null
|
let timeoutId = null
|
||||||
|
// Compatibility: Handle "user role" and "no role" identically
|
||||||
function termChanged(sleep = throttle) {
|
function termChanged(sleep = throttle) {
|
||||||
if (authlevel == roles.user) {
|
if (authlevel >= roles.manager) {
|
||||||
project = term
|
|
||||||
|
|
||||||
if (timeoutId != null)
|
|
||||||
clearTimeout(timeoutId)
|
|
||||||
|
|
||||||
timeoutId = setTimeout(() => {
|
|
||||||
dispatch('update', {
|
|
||||||
project
|
|
||||||
})
|
|
||||||
}, sleep)
|
|
||||||
} else if (authlevel >= roles.manager) {
|
|
||||||
if (mode == 'user')
|
if (mode == 'user')
|
||||||
user = term
|
user = term
|
||||||
else
|
else
|
||||||
@ -50,17 +40,21 @@
|
|||||||
project
|
project
|
||||||
})
|
})
|
||||||
}, sleep)
|
}, sleep)
|
||||||
|
} else {
|
||||||
|
project = term
|
||||||
|
if (timeoutId != null)
|
||||||
|
clearTimeout(timeoutId)
|
||||||
|
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
dispatch('update', {
|
||||||
|
project
|
||||||
|
})
|
||||||
|
}, sleep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if authlevel == roles.user}
|
{#if authlevel >= roles.manager}
|
||||||
<InputGroup>
|
|
||||||
<Input
|
|
||||||
type="text" bind:value={term} on:change={() => termChanged()} on:keyup={(event) => termChanged(event.key == 'Enter' ? 0 : throttle)} placeholder='filter project...'
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
{:else if authlevel >= roles.manager}
|
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<select style="max-width: 175px;" class="form-select"
|
<select style="max-width: 175px;" class="form-select"
|
||||||
bind:value={mode} on:change={modeChanged}>
|
bind:value={mode} on:change={modeChanged}>
|
||||||
@ -72,5 +66,10 @@
|
|||||||
placeholder={mode == 'user' ? 'filter username...' : 'filter project...'} />
|
placeholder={mode == 'user' ? 'filter username...' : 'filter project...'} />
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
{:else}
|
{:else}
|
||||||
Unauthorized
|
<!-- Compatibility: Handle "user role" and "no role" identically-->
|
||||||
|
<InputGroup>
|
||||||
|
<Input
|
||||||
|
type="text" bind:value={term} on:change={() => termChanged()} on:keyup={(event) => termChanged(event.key == 'Enter' ? 0 : throttle)} placeholder='filter project...'
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
{/if}
|
{/if}
|
||||||
|
Loading…
Reference in New Issue
Block a user