smol updates

pull/1/head
plazmoid 2 years ago
parent 0a077af936
commit a594348a30
  1. 2
      Makefile.toml
  2. 1
      bin/u_agent/src/lib.rs
  3. 5
      bin/u_panel/Cargo.toml
  4. 2
      bin/u_panel/src/server/fe/package.json
  5. 6
      bin/u_panel/src/server/fe/src/app/app.module.ts
  6. 26
      bin/u_panel/src/server/fe/src/app/core/tables/agent.component.ts
  7. 12
      bin/u_panel/src/server/fe/src/app/core/tables/dialogs/agent-info-dialog.html
  8. 11
      bin/u_panel/src/server/fe/src/app/core/tables/dialogs/agent_info.component.ts
  9. 1
      bin/u_panel/src/server/fe/src/app/core/tables/dialogs/index.ts
  10. 12
      bin/u_panel/src/server/fe/src/app/core/tables/job.component.ts
  11. 15
      bin/u_panel/src/server/fe/src/app/core/tables/result.component.ts
  12. 3
      bin/u_panel/src/server/fe/src/app/core/tables/table.component.html
  13. 5
      bin/u_panel/src/server/fe/src/app/core/tables/table.component.less
  14. 6
      bin/u_panel/src/server/fe/src/app/core/tables/table.component.ts
  15. 3
      bin/u_panel/src/server/fe/src/app/core/utils.ts
  16. 6
      bin/u_server/src/db.rs
  17. 3
      images/integration-tests/u_db.Dockerfile
  18. 5
      integration/docker_compose.py
  19. 20
      integration/integration_tests.py
  20. 4
      lib/u_lib/src/api.rs
  21. 7
      todos.txt

@ -71,7 +71,7 @@ args = ["test", "--target", "${TARGET}", "--lib", "--", "${@}"]
[tasks.integration] [tasks.integration]
script = ''' script = '''
echo "!!! This task doesn't perform project rebuild, trigger it manually if need" [[ ! -d "./target/${TARGET}/${PROFILE_OVERRIDE}" ]] && echo 'No target folder. Build project first' && exit 1
cd ./integration cd ./integration
bash integration_tests.sh ${@} bash integration_tests.sh ${@}
''' '''

@ -1,7 +1,6 @@
// TODO: // TODO:
// поддержка питона // поддержка питона
// резолв адреса управляющего сервера через DoT // резолв адреса управляющего сервера через DoT
// кроссплатформенность (реализовать интерфейс для винды и никсов)
#[macro_use] #[macro_use]
extern crate log; extern crate log;

@ -8,20 +8,15 @@ edition = "2021"
[dependencies] [dependencies]
actix-web = "4.1" actix-web = "4.1"
backtrace = "0.3.61"
structopt = "0.3.21" structopt = "0.3.21"
uuid = "0.6.5" uuid = "0.6.5"
serde_json = "1.0.4" serde_json = "1.0.4"
serde = { version = "1.0.114", features = ["derive"] } serde = { version = "1.0.114", features = ["derive"] }
tokio = { version = "1.11.0", features = ["rt", "rt-multi-thread"] } tokio = { version = "1.11.0", features = ["rt", "rt-multi-thread"] }
u_lib = { version = "*", path = "../../lib/u_lib", features = ["panel"] } u_lib = { version = "*", path = "../../lib/u_lib", features = ["panel"] }
tui = { version = "0.16", default-features = false, features = ['crossterm'] }
crossterm = "0.22.1"
anyhow = "1.0.44" anyhow = "1.0.44"
strum = { version = "0.22.0", features = ["derive"] } strum = { version = "0.22.0", features = ["derive"] }
once_cell = "1.8.0" once_cell = "1.8.0"
crossbeam = "0.8.1"
async-channel = "1.6.1"
tracing = "0.1.29" tracing = "0.1.29"
tracing-subscriber = { version = "0.3.3", features = ["env-filter"]} tracing-subscriber = { version = "0.3.3", features = ["env-filter"]}
signal-hook = "0.3.12" signal-hook = "0.3.12"

@ -27,7 +27,7 @@
"zone.js": "~0.11.4" "zone.js": "~0.11.4"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~13.1.2", "@angular-devkit/build-angular": "^13.3.9",
"@angular/cli": "~13.1.2", "@angular/cli": "~13.1.2",
"@angular/compiler-cli": "~13.1.0", "@angular/compiler-cli": "~13.1.0",
"@types/jasmine": "~3.10.0", "@types/jasmine": "~3.10.0",

@ -10,14 +10,17 @@ import { MatButtonModule } from '@angular/material/button'
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { MatDialogModule } from '@angular/material/dialog';
import { AgentComponent, JobComponent, ResultComponent } from './core/tables'; import { AgentComponent, JobComponent, ResultComponent } from './core/tables';
import { AgentInfoDialogComponent } from './core/tables/dialogs';
@NgModule({ @NgModule({
declarations: [ declarations: [
AppComponent, AppComponent,
AgentComponent, AgentComponent,
JobComponent, JobComponent,
ResultComponent ResultComponent,
AgentInfoDialogComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -28,6 +31,7 @@ import { AgentComponent, JobComponent, ResultComponent } from './core/tables';
MatButtonModule, MatButtonModule,
MatFormFieldModule, MatFormFieldModule,
MatInputModule, MatInputModule,
MatDialogModule,
MatProgressSpinnerModule, MatProgressSpinnerModule,
BrowserAnimationsModule BrowserAnimationsModule
], ],

@ -1,6 +1,10 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { TablesComponent } from './table.component'; import { TablesComponent } from './table.component';
import { AgentModel } from '../models'; import { AgentModel } from '../models';
import { AgentInfoDialogComponent } from './dialogs/agent_info.component';
import { HttpClient } from '@angular/common/http';
import { MatDialog } from '@angular/material/dialog';
import { epochToStr } from '../utils';
@Component({ @Component({
selector: 'agent-table', selector: 'agent-table',
@ -8,34 +12,46 @@ import { AgentModel } from '../models';
styleUrls: ['./table.component.less'] styleUrls: ['./table.component.less']
}) })
export class AgentComponent extends TablesComponent<AgentModel> { export class AgentComponent extends TablesComponent<AgentModel> {
constructor(public override _httpClient: HttpClient, public override info_dlg: MatDialog) {
super(_httpClient, info_dlg);
}
area = 'agents' as const; area = 'agents' as const;
columns = [ columns = [
{ {
def: "id", def: "id",
name: "ID", name: "ID",
cell: (cell: AgentModel) => `${cell.id}` cell: (cell: AgentModel) => cell.id
}, },
{ {
def: "alias", def: "alias",
name: "Alias", name: "Alias",
cell: (cell: AgentModel) => `${cell.alias}` cell: (cell: AgentModel) => cell.alias ?? ""
}, },
{ {
def: "username", def: "username",
name: "User", name: "User",
cell: (cell: AgentModel) => `${cell.username}` cell: (cell: AgentModel) => cell.username
}, },
{ {
def: "hostname", def: "hostname",
name: "Host", name: "Host",
cell: (cell: AgentModel) => `${cell.hostname}` cell: (cell: AgentModel) => cell.hostname
}, },
{ {
def: "last_active", def: "last_active",
name: "Last active", name: "Last active",
cell: (cell: AgentModel) => `${cell.last_active.secs_since_epoch}` cell: (cell: AgentModel) => epochToStr(cell.last_active.secs_since_epoch)
}, },
] ]
displayedColumns = this.columns.map((c) => c.def); displayedColumns = this.columns.map((c) => c.def);
show_item_dialog(obj: AgentModel) {
const dialog = this.info_dlg.open(AgentInfoDialogComponent, {
data: obj
});
}
} }

@ -0,0 +1,12 @@
<mat-dialog-content>
<div>
<p>Alias: {{data.alias}}</p>
<p>Username: {{data.username}}</p>
<p>Hostname: {{data.hostname}}</p>
<p>Platform: {{data.platform}}</p>
</div>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button cdkFocusInitial>Edit</button>
<button mat-button mat-dialog-close>Cancel</button>
</mat-dialog-actions>

@ -0,0 +1,11 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { AgentModel } from '../../models/agent.model';
@Component({
selector: 'agent-info-dialog',
templateUrl: 'agent-info-dialog.html',
})
export class AgentInfoDialogComponent {
constructor(@Inject(MAT_DIALOG_DATA) public data: AgentModel) { }
}

@ -0,0 +1 @@
export * from './agent_info.component';

@ -14,22 +14,22 @@ export class JobComponent extends TablesComponent<JobModel> {
{ {
def: "id", def: "id",
name: "ID", name: "ID",
cell: (cell: JobModel) => `${cell.id}` cell: (cell: JobModel) => cell.id
}, },
{ {
def: "alias", def: "alias",
name: "Alias", name: "Alias",
cell: (cell: JobModel) => `${cell.alias}` cell: (cell: JobModel) => cell.alias
}, },
{ {
def: "argv", def: "argv",
name: "Cmd-line args", name: "Cmd-line args",
cell: (cell: JobModel) => `${cell.argv}` cell: (cell: JobModel) => cell.argv
}, },
{ {
def: "platform", def: "platform",
name: "Platform", name: "Platform",
cell: (cell: JobModel) => `${cell.platform}` cell: (cell: JobModel) => cell.platform
}, },
{ {
def: "payload", def: "payload",
@ -39,8 +39,10 @@ export class JobComponent extends TablesComponent<JobModel> {
{ {
def: "etype", def: "etype",
name: "Type", name: "Type",
cell: (cell: JobModel) => `${cell.exec_type}` cell: (cell: JobModel) => cell.exec_type
}, },
] ]
displayedColumns = this.columns.map((c) => c.def); displayedColumns = this.columns.map((c) => c.def);
show_item_dialog(obj: JobModel) { }
} }

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { TablesComponent } from './table.component'; import { TablesComponent } from './table.component';
import { ResultModel } from '../models'; import { ResultModel } from '../models';
import { epochToStr } from '../utils';
@Component({ @Component({
selector: 'result-table', selector: 'result-table',
@ -14,22 +15,22 @@ export class ResultComponent extends TablesComponent<ResultModel> {
{ {
def: "id", def: "id",
name: "ID", name: "ID",
cell: (cell: ResultModel) => `${cell.id}` cell: (cell: ResultModel) => cell.id
}, },
{ {
def: "alias", def: "alias",
name: "Alias", name: "Alias",
cell: (cell: ResultModel) => `${cell.alias}` cell: (cell: ResultModel) => cell.alias
}, },
{ {
def: "agent_id", def: "agent_id",
name: "Agent ID", name: "Agent ID",
cell: (cell: ResultModel) => `${cell.agent_id}` cell: (cell: ResultModel) => cell.agent_id
}, },
{ {
def: "job_id", def: "job_id",
name: "Job ID", name: "Job ID",
cell: (cell: ResultModel) => `${cell.job_id}` cell: (cell: ResultModel) => cell.job_id
}, },
{ {
def: "state", def: "state",
@ -39,8 +40,12 @@ export class ResultComponent extends TablesComponent<ResultModel> {
{ {
def: "last_updated", def: "last_updated",
name: "Last updated", name: "Last updated",
cell: (cell: ResultModel) => `${cell.updated.secs_since_epoch}` cell: (cell: ResultModel) => epochToStr(cell.updated.secs_since_epoch)
}, },
] ]
displayedColumns = this.columns.map((c) => c.def); displayedColumns = this.columns.map((c) => c.def);
show_item_dialog(obj: ResultModel) {
}
} }

@ -23,7 +23,8 @@
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> <tr mat-row class="data-table-row" *matRowDef="let row; columns: displayedColumns;"
(click)="show_item_dialog(row)"></tr>
<tr class="mat-row" *matNoDataRow> <tr class="mat-row" *matNoDataRow>
<td class="mat-cell">No data</td> <td class="mat-cell">No data</td>

@ -22,3 +22,8 @@
#refresh_btn { #refresh_btn {
margin-left: 10px; margin-left: 10px;
} }
.data-table-row:hover {
background: whitesmoke;
cursor: pointer;
}

@ -4,6 +4,7 @@ import { catchError, map, startWith, switchMap } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { ApiTableService } from '../'; import { ApiTableService } from '../';
import { MatTableDataSource } from '@angular/material/table'; import { MatTableDataSource } from '@angular/material/table';
import { MatDialog } from '@angular/material/dialog';
@Directive() @Directive()
export abstract class TablesComponent<T> implements OnInit { export abstract class TablesComponent<T> implements OnInit {
@ -13,7 +14,7 @@ export abstract class TablesComponent<T> implements OnInit {
isLoadingResults = true; isLoadingResults = true;
constructor(private _httpClient: HttpClient) { constructor(public _httpClient: HttpClient, public info_dlg: MatDialog) {
this.table_data = new MatTableDataSource; this.table_data = new MatTableDataSource;
} }
@ -37,7 +38,7 @@ export abstract class TablesComponent<T> implements OnInit {
this.isLoadingResults = false; this.isLoadingResults = false;
if (data === null) { if (data === null) {
return []; return "no data returned"
} }
// Only refresh the result length if there is new data. In case of rate // Only refresh the result length if there is new data. In case of rate
@ -54,6 +55,7 @@ export abstract class TablesComponent<T> implements OnInit {
this.table_data.filter = filterValue.trim().toLowerCase(); this.table_data.filter = filterValue.trim().toLowerCase();
} }
abstract show_item_dialog(obj: T): void;
abstract columns: ColumnDef<T>[]; abstract columns: ColumnDef<T>[];
abstract displayedColumns: string[]; abstract displayedColumns: string[];

@ -0,0 +1,3 @@
export function epochToStr(epoch: number): string {
return new Date(epoch * 1000).toLocaleString('en-GB')
}

@ -2,7 +2,7 @@ use crate::errors::{Error, SResult};
use diesel::{pg::PgConnection, prelude::*, result::Error as DslError}; use diesel::{pg::PgConnection, prelude::*, result::Error as DslError};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use serde::Deserialize; use serde::Deserialize;
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Mutex, MutexGuard};
use u_lib::{ use u_lib::{
models::{schema, Agent, AssignedJob, JobMeta, JobState}, models::{schema, Agent, AssignedJob, JobMeta, JobState},
utils::load_env, utils::load_env,
@ -13,7 +13,7 @@ pub struct UDB {
pub conn: PgConnection, pub conn: PgConnection,
} }
static DB: OnceCell<Arc<Mutex<UDB>>> = OnceCell::new(); static DB: OnceCell<Mutex<UDB>> = OnceCell::new();
#[derive(Deserialize)] #[derive(Deserialize)]
struct DBEnv { struct DBEnv {
@ -34,7 +34,7 @@ impl UDB {
let instance = UDB { let instance = UDB {
conn: PgConnection::establish(&db_url).unwrap(), conn: PgConnection::establish(&db_url).unwrap(),
}; };
Arc::new(Mutex::new(instance)) Mutex::new(instance)
}) })
.lock() .lock()
.unwrap() .unwrap()

@ -1,5 +1,6 @@
FROM postgres:13.3 FROM postgres:14.5
ENV DEBIAN_FRONTEND=noninteractive
RUN apt update && apt upgrade -y RUN apt update && apt upgrade -y
RUN apt install -y curl build-essential libpq-dev iproute2 RUN apt install -y curl build-essential libpq-dev iproute2
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable --profile minimal RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable --profile minimal

@ -15,7 +15,8 @@ class Compose:
def __init__(self): def __init__(self):
self.container_tpl = 'integration_%s_%d' self.container_tpl = 'integration_%s_%d'
self.cmd_container = self.container_tpl % ('tests_runner', 1) self.cmd_container = self.container_tpl % ('tests_runner', 1)
self.ALL_CONTAINERS = [self.container_tpl % (c, 1) for c in self.ALL_CONTAINERS] self.ALL_CONTAINERS = [self.container_tpl %
(c, 1) for c in self.ALL_CONTAINERS]
self.scaled_svc = {} self.scaled_svc = {}
self.scale("u_agent", 2) self.scale("u_agent", 2)
@ -28,7 +29,7 @@ class Compose:
def _call(self, *args): def _call(self, *args):
cmd = [ cmd = [
'docker-compose', 'docker-compose',
'--no-ansi', '--ansi=never',
] + list(args) ] + list(args)
log(f'Running docker-compose command: {cmd}') log(f'Running docker-compose command: {cmd}')
subprocess.check_call(cmd) subprocess.check_call(cmd)

@ -14,21 +14,35 @@ def abort_handler(s, _):
cluster.down() cluster.down()
def usage_exit():
usage = f"""Usage:
python {__file__.split('/')[-1]} [--rebuild] [--preserve] [--no-run]"""
print(usage)
sys.exit(1)
def run_tests(): def run_tests():
force_rebuild = '--rebuild' in sys.argv allowed_args = set(["--rebuild", "--preserve", "--no-run"])
preserve_containers = '--preserve' in sys.argv args = sys.argv[1:]
if not set(args).issubset(allowed_args):
usage_exit()
force_rebuild = '--rebuild' in args
preserve_containers = '--preserve' in args
only_setup_cluster = '--no-run' in args
for s in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP): for s in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP):
signal.signal(s, abort_handler) signal.signal(s, abort_handler)
rebuild_images_if_needed(force_rebuild) rebuild_images_if_needed(force_rebuild)
try: try:
cluster.up() cluster.up()
cluster.is_alive() cluster.is_alive()
if not only_setup_cluster:
cluster.run('cargo test --test integration') cluster.run('cargo test --test integration')
except Exception as e: except Exception as e:
err(e) err(e)
sys.exit(1) sys.exit(1)
finally: finally:
if not preserve_containers: if not preserve_containers and not only_setup_cluster:
cluster.down() cluster.down()

@ -46,9 +46,9 @@ impl ClientHandler {
} }
} }
async fn _req<P: AsMsg + Debug, M: AsMsg + DeserializeOwned + Debug + Default>( async fn _req<P: AsMsg, M: AsMsg + DeserializeOwned + Default>(
&self, &self,
url: impl AsRef<str> + Debug, url: impl AsRef<str>,
payload: P, payload: P,
) -> Result<M> { ) -> Result<M> {
let request = self let request = self

@ -0,0 +1,7 @@
Upload/download files
More tests
Agent update (use more JobType's)
Erase log macros in release mode
Bump wine version to test agent on windows
Store downloaded payload on disk instead of memory
Improve web interface
Loading…
Cancel
Save