diff --git a/components/crud-web-apps/tensorboards/backend/app/routes/get.py b/components/crud-web-apps/tensorboards/backend/app/routes/get.py index e8d5a9eeb9d..e54349b1ca1 100644 --- a/components/crud-web-apps/tensorboards/backend/app/routes/get.py +++ b/components/crud-web-apps/tensorboards/backend/app/routes/get.py @@ -27,3 +27,25 @@ def get_pvcs(namespace): content = [pvc.metadata.name for pvc in pvcs.items] return api.success_response("pvcs", content) + + +@bp.route("/api/namespaces//poddefaults") +def get_poddefaults(namespace): + pod_defaults = api.list_poddefaults(namespace) + + # Return a list of pod defaults adding custom fields (label, desc) for forms + contents = [] + for pd in pod_defaults["items"]: + label = list(pd["spec"]["selector"]["matchLabels"].keys())[0] + if "desc" in pd["spec"]: + desc = pd["spec"]["desc"] + else: + desc = pd["metadata"]["name"] + + pd["label"] = label + pd["desc"] = desc + contents.append(pd) + + log.info("Found poddefaults: %s", contents) + + return api.success_response("poddefaults", contents) diff --git a/components/crud-web-apps/tensorboards/backend/app/routes/post.py b/components/crud-web-apps/tensorboards/backend/app/routes/post.py index 8a637888aa9..b0ed5db7073 100644 --- a/components/crud-web-apps/tensorboards/backend/app/routes/post.py +++ b/components/crud-web-apps/tensorboards/backend/app/routes/post.py @@ -17,7 +17,7 @@ def post_tensorboard(namespace): body = request.get_json() - log.info("Got body: ", body) + log.info("Got body: %s", body) name = body["name"] diff --git a/components/crud-web-apps/tensorboards/backend/app/utils.py b/components/crud-web-apps/tensorboards/backend/app/utils.py index 3e4bbe48fca..5941070a1a3 100644 --- a/components/crud-web-apps/tensorboards/backend/app/utils.py +++ b/components/crud-web-apps/tensorboards/backend/app/utils.py @@ -1,5 +1,5 @@ from kubeflow.kubeflow.crud_backend import status - +from werkzeug.exceptions import BadRequest def parse_tensorboard(tensorboard): """ @@ -27,12 +27,31 @@ def get_tensorboard_dict(namespace, body): """ Create Tensorboard object from request body and format it as a Python dict. """ + metadata = { + "name": body["name"], + "namespace": namespace, + } + labels = get_tensorboard_configurations(body=body) + if labels: + metadata["labels"] = labels tensorboard = { "apiVersion": "tensorboard.kubeflow.org/v1alpha1", "kind": "Tensorboard", - "metadata": {"name": body["name"], "namespace": namespace, }, - "spec": {"logspath": body["logspath"], }, + "metadata": metadata, + "spec": {"logspath": body["logspath"]}, } return tensorboard + +def get_tensorboard_configurations(body): + labels = body.get("configurations", None) + cr_labels = {} + + if not isinstance(labels, list): + raise BadRequest("Labels for PodDefaults are not list: %s" % labels) + + for label in labels: + cr_labels[label] = "true" + + return cr_labels \ No newline at end of file diff --git a/components/crud-web-apps/tensorboards/deploy/app-rbac.yaml b/components/crud-web-apps/tensorboards/deploy/app-rbac.yaml index 273639fc967..08b6c4ae3bb 100644 --- a/components/crud-web-apps/tensorboards/deploy/app-rbac.yaml +++ b/components/crud-web-apps/tensorboards/deploy/app-rbac.yaml @@ -38,6 +38,14 @@ rules: - watch - create - delete +- apiGroups: + - kubeflow.org + resources: + - poddefaults + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding diff --git a/components/crud-web-apps/tensorboards/frontend/src/app/app.module.ts b/components/crud-web-apps/tensorboards/frontend/src/app/app.module.ts index dfd640b4bb8..bcfe3b9a9e6 100644 --- a/components/crud-web-apps/tensorboards/frontend/src/app/app.module.ts +++ b/components/crud-web-apps/tensorboards/frontend/src/app/app.module.ts @@ -20,7 +20,7 @@ import { import { IndexComponent } from './pages/index/index.component'; import { FormComponent } from './pages/form/form.component'; - +import { FormConfigurationsModule } from './pages/form/form-configurations/form-configurations.module'; import { HttpClientModule, HttpClient } from '@angular/common/http'; @NgModule({ @@ -38,6 +38,7 @@ import { HttpClientModule, HttpClient } from '@angular/common/http'; FormModule, KubeflowModule, HttpClientModule, + FormConfigurationsModule, ], providers: [ { provide: ErrorStateMatcher, useClass: ImmediateErrorStateMatcher }, diff --git a/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.html b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.html new file mode 100644 index 00000000000..8379e75cc3a --- /dev/null +++ b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.html @@ -0,0 +1,10 @@ + + + Configurations + + + {{ config.desc }} + + + + diff --git a/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.scss b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.spec.ts b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.spec.ts new file mode 100644 index 00000000000..7f822ea23c8 --- /dev/null +++ b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.spec.ts @@ -0,0 +1,59 @@ +import { CommonModule } from '@angular/common'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { FormControl, FormGroup } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatSelectModule } from '@angular/material/select'; +import { NamespaceService } from 'kubeflow'; +import { of } from 'rxjs'; +import { TWABackendService } from 'src/app/services/backend.service'; +import { FormModule as KfFormModule } from 'kubeflow'; +import { FormConfigurationsComponent } from './form-configurations.component'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +const TWABackendServiceStub: Partial = { + getPodDefaults: () => of(), +}; + +const NamespaceServiceStub: Partial = { + getSelectedNamespace: () => of(), +}; + +describe('FormConfigurationsComponent', () => { + let component: FormConfigurationsComponent; + let fixture: ComponentFixture; + + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [FormConfigurationsComponent], + imports: [ + CommonModule, + KfFormModule, + MatFormFieldModule, + MatInputModule, + MatSelectModule, + NoopAnimationsModule, + ], + providers: [ + { provide: TWABackendService, useValue: TWABackendServiceStub }, + { provide: NamespaceService, useValue: NamespaceServiceStub }, + ], + }).compileComponents(); + }), + ); + + beforeEach(() => { + fixture = TestBed.createComponent(FormConfigurationsComponent); + component = fixture.componentInstance; + component.parentForm = new FormGroup({ + configurations: new FormControl(), + }); + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.ts b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.ts new file mode 100644 index 00000000000..dcbd062f8d5 --- /dev/null +++ b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.component.ts @@ -0,0 +1,36 @@ +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { PodDefault } from 'src/app/types'; +import { Subscription } from 'rxjs'; +import { NamespaceService } from 'kubeflow'; +import { TWABackendService } from 'src/app/services/backend.service'; + +@Component({ + selector: 'app-form-configurations', + templateUrl: './form-configurations.component.html', + styleUrls: ['./form-configurations.component.scss'], +}) +export class FormConfigurationsComponent implements OnInit, OnDestroy { + podDefaults: PodDefault[]; + subscriptions = new Subscription(); + + @Input() parentForm: FormGroup; + + constructor(public ns: NamespaceService, public backend: TWABackendService) {} + + ngOnInit() { + // Keep track of the selected namespace + const nsSub = this.ns.getSelectedNamespace().subscribe(namespace => { + // Get the PodDefaults of the new Namespace + this.backend.getPodDefaults(namespace).subscribe(pds => { + this.podDefaults = pds; + }); + }); + + this.subscriptions.add(nsSub); + } + + ngOnDestroy() { + this.subscriptions.unsubscribe(); + } +} diff --git a/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.module.ts b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.module.ts new file mode 100644 index 00000000000..3594c8215c4 --- /dev/null +++ b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form-configurations/form-configurations.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormModule as KfFormModule } from 'kubeflow'; +import { FormConfigurationsComponent } from './form-configurations.component'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatInputModule } from '@angular/material/input'; +import { MatSelectModule } from '@angular/material/select'; + +@NgModule({ + declarations: [FormConfigurationsComponent], + imports: [ + CommonModule, + KfFormModule, + MatFormFieldModule, + ReactiveFormsModule, + MatInputModule, + MatSelectModule, + ], + exports: [FormConfigurationsComponent], +}) +export class FormConfigurationsModule {} diff --git a/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form.component.html b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form.component.html index bf2faf3c3a0..1abd8462776 100644 --- a/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form.component.html +++ b/components/crud-web-apps/tensorboards/frontend/src/app/pages/form/form.component.html @@ -72,6 +72,8 @@ + +