mirror of
https://github.com/hanzo-ml/katib.git
synced 2026-09-24 00:31:27 +00:00
[UI] Remove Deprecated Katib UI (#2179)
* [UI] Remove Deprecated Katib UI * Fix UI Developer doc
This commit is contained in:
committed by
GitHub
parent
6ab4b6ba67
commit
7d16660c2c
@@ -4,5 +4,3 @@ docs
|
||||
manifests
|
||||
pkg/ui/*/frontend/node_modules
|
||||
pkg/ui/*/frontend/build
|
||||
pkg/new-ui/*/frontend/node_modules
|
||||
pkg/new-ui/*/frontend/build
|
||||
|
||||
@@ -3,7 +3,7 @@ name: E2E Test with darts-cnn-cifar10
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -3,7 +3,7 @@ name: E2E Test with enas-cnn-cifar10
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -3,7 +3,7 @@ name: E2E Test with mxnet-mnist
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -3,7 +3,7 @@ name: E2E Test with pytorch-mnist
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -3,7 +3,7 @@ name: E2E Test with simple-pbt
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -3,7 +3,7 @@ name: E2E Test with tf-mnist-with-summaries
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
jobs:
|
||||
algorithm:
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- component-name: katib-db-manager
|
||||
dockerfile: cmd/db-manager/v1beta1/Dockerfile
|
||||
- component-name: katib-ui
|
||||
dockerfile: cmd/new-ui/v1beta1/Dockerfile
|
||||
dockerfile: cmd/ui/v1beta1/Dockerfile
|
||||
- component-name: cert-generator
|
||||
dockerfile: cmd/cert-generator/v1beta1/Dockerfile
|
||||
- component-name: file-metrics-collector
|
||||
|
||||
@@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
jobs:
|
||||
trial:
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Go Test
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Lint Files
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Frontend Test
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- pkg/new-ui/v1beta1/frontend/**
|
||||
- pkg/ui/v1beta1/frontend/**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -25,12 +25,12 @@ jobs:
|
||||
|
||||
- name: Format katib code
|
||||
run: |
|
||||
npm install prettier --prefix ./pkg/new-ui/v1beta1/frontend
|
||||
npm install prettier --prefix ./pkg/ui/v1beta1/frontend
|
||||
make prettier-check
|
||||
|
||||
- name: Lint katib code
|
||||
run: |
|
||||
cd pkg/new-ui/v1beta1/frontend
|
||||
cd pkg/ui/v1beta1/frontend
|
||||
npm run lint-check
|
||||
|
||||
frontend-unit-tests:
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Fetch Kubeflow and install common code dependencies
|
||||
run: |
|
||||
COMMIT=$(cat pkg/new-ui/v1beta1/frontend/COMMIT)
|
||||
COMMIT=$(cat pkg/ui/v1beta1/frontend/COMMIT)
|
||||
cd /tmp && git clone https://github.com/kubeflow/kubeflow.git
|
||||
cd kubeflow
|
||||
git checkout $COMMIT
|
||||
@@ -59,13 +59,13 @@ jobs:
|
||||
|
||||
- name: Install KWA dependencies
|
||||
run: |
|
||||
cd pkg/new-ui/v1beta1/frontend
|
||||
cd pkg/ui/v1beta1/frontend
|
||||
npm i
|
||||
npm link kubeflow
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
cd pkg/new-ui/v1beta1/frontend
|
||||
cd pkg/ui/v1beta1/frontend
|
||||
npm run test:prod
|
||||
|
||||
frontend-ui-tests:
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
|
||||
- name: Fetch Kubeflow and install common code dependencies
|
||||
run: |
|
||||
COMMIT=$(cat pkg/new-ui/v1beta1/frontend/COMMIT)
|
||||
COMMIT=$(cat pkg/ui/v1beta1/frontend/COMMIT)
|
||||
cd /tmp && git clone https://github.com/kubeflow/kubeflow.git
|
||||
cd kubeflow
|
||||
git checkout $COMMIT
|
||||
@@ -91,11 +91,11 @@ jobs:
|
||||
npm link ./dist/kubeflow
|
||||
- name: Install KWA dependencies
|
||||
run: |
|
||||
cd pkg/new-ui/v1beta1/frontend
|
||||
cd pkg/ui/v1beta1/frontend
|
||||
npm i
|
||||
npm link kubeflow
|
||||
- name: Serve UI & run Cypress tests in Chrome and Firefox
|
||||
run: |
|
||||
cd pkg/new-ui/v1beta1/frontend
|
||||
cd pkg/ui/v1beta1/frontend
|
||||
npm run start & npx wait-on http://localhost:4200
|
||||
npm run ui-test-ci-all
|
||||
|
||||
@@ -3,7 +3,7 @@ name: Python Test
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "pkg/new-ui/v1beta1/frontend/**"
|
||||
- "pkg/ui/v1beta1/frontend/**"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -143,7 +143,7 @@ endif
|
||||
|
||||
# Prettier UI format check for Katib v1beta1.
|
||||
prettier-check:
|
||||
npm run format:check --prefix pkg/new-ui/v1beta1/frontend
|
||||
npm run format:check --prefix pkg/ui/v1beta1/frontend
|
||||
|
||||
# Update boilerplate for the source code.
|
||||
update-boilerplate:
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# --- Clone the kubeflow/kubeflow code ---
|
||||
FROM ubuntu AS fetch-kubeflow-kubeflow
|
||||
|
||||
RUN apt-get update && apt-get install git -y
|
||||
|
||||
WORKDIR /kf
|
||||
COPY ./pkg/new-ui/v1beta1/frontend/COMMIT ./
|
||||
RUN git clone https://github.com/kubeflow/kubeflow.git && \
|
||||
COMMIT=$(cat ./COMMIT) && \
|
||||
cd kubeflow && \
|
||||
git checkout $COMMIT
|
||||
|
||||
# --- Build the frontend kubeflow library ---
|
||||
FROM node:12 AS frontend-kubeflow-lib
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
ARG LIB=/kf/kubeflow/components/crud-web-apps/common/frontend/kubeflow-common-lib
|
||||
COPY --from=fetch-kubeflow-kubeflow $LIB/package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY --from=fetch-kubeflow-kubeflow $LIB/ ./
|
||||
RUN npm run build
|
||||
|
||||
# --- Build the frontend ---
|
||||
FROM node:12 AS frontend
|
||||
|
||||
WORKDIR /src
|
||||
COPY ./pkg/new-ui/v1beta1/frontend/package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY ./pkg/new-ui/v1beta1/frontend/ .
|
||||
COPY --from=frontend-kubeflow-lib /src/dist/kubeflow/ ./node_modules/kubeflow/
|
||||
|
||||
RUN npm run build:prod
|
||||
|
||||
# --- Build the backend ---
|
||||
FROM golang:alpine AS go-build
|
||||
|
||||
ARG TARGETARCH
|
||||
|
||||
WORKDIR /go/src/github.com/kubeflow/katib
|
||||
|
||||
# Download packages.
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
RUN go mod download -x
|
||||
|
||||
# Copy sources.
|
||||
COPY cmd/ cmd/
|
||||
COPY pkg/ pkg/
|
||||
|
||||
# Build the binary.
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -a -o katib-ui ./cmd/new-ui/v1beta1
|
||||
|
||||
# --- Compose the web app ---
|
||||
FROM alpine:3.15
|
||||
WORKDIR /app
|
||||
COPY --from=go-build /go/src/github.com/kubeflow/katib/katib-ui /app/
|
||||
COPY --from=frontend /src/dist/static /app/build/static/
|
||||
ENTRYPOINT ["./katib-ui"]
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The Kubeflow Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
|
||||
common_v1beta1 "github.com/kubeflow/katib/pkg/common/v1beta1"
|
||||
ui "github.com/kubeflow/katib/pkg/new-ui/v1beta1"
|
||||
)
|
||||
|
||||
var (
|
||||
port, host, buildDir, dbManagerAddr *string
|
||||
)
|
||||
|
||||
func init() {
|
||||
port = flag.String("port", "8080", "The port to listen to for incoming HTTP connections")
|
||||
host = flag.String("host", "0.0.0.0", "The host to listen to for incoming HTTP connections")
|
||||
buildDir = flag.String("build-dir", "/app/build", "The dir of frontend")
|
||||
dbManagerAddr = flag.String("db-manager-address", common_v1beta1.GetDBManagerAddr(), "The address of Katib DB manager")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
kuh := ui.NewKatibUIHandler(*dbManagerAddr)
|
||||
|
||||
log.Printf("Serving the frontend dir %s", *buildDir)
|
||||
frontend := http.FileServer(http.Dir(*buildDir))
|
||||
http.HandleFunc("/katib/", kuh.ServeIndex(*buildDir))
|
||||
http.Handle("/katib/static/", http.StripPrefix("/katib/", frontend))
|
||||
|
||||
http.HandleFunc("/katib/fetch_experiments/", kuh.FetchExperiments)
|
||||
|
||||
http.HandleFunc("/katib/create_experiment/", kuh.CreateExperiment)
|
||||
|
||||
http.HandleFunc("/katib/delete_experiment/", kuh.DeleteExperiment)
|
||||
|
||||
http.HandleFunc("/katib/fetch_experiment/", kuh.FetchExperiment)
|
||||
http.HandleFunc("/katib/fetch_trial/", kuh.FetchTrial)
|
||||
http.HandleFunc("/katib/fetch_suggestion/", kuh.FetchSuggestion)
|
||||
|
||||
http.HandleFunc("/katib/fetch_hp_job_info/", kuh.FetchHPJobInfo)
|
||||
http.HandleFunc("/katib/fetch_hp_job_trial_info/", kuh.FetchHPJobTrialInfo)
|
||||
http.HandleFunc("/katib/fetch_nas_job_info/", kuh.FetchNASJobInfo)
|
||||
|
||||
http.HandleFunc("/katib/fetch_trial_templates/", kuh.FetchTrialTemplates)
|
||||
http.HandleFunc("/katib/add_template/", kuh.AddTemplate)
|
||||
http.HandleFunc("/katib/edit_template/", kuh.EditTemplate)
|
||||
http.HandleFunc("/katib/delete_template/", kuh.DeleteTemplate)
|
||||
http.HandleFunc("/katib/fetch_namespaces", kuh.FetchNamespaces)
|
||||
http.HandleFunc("/katib/fetch_trial_logs/", kuh.FetchTrialLogs)
|
||||
|
||||
log.Printf("Serving at %s:%s", *host, *port)
|
||||
if err := http.ListenAndServe(fmt.Sprintf("%s:%s", *host, *port), nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
+38
-11
@@ -1,13 +1,40 @@
|
||||
# Build the Katib UI.
|
||||
FROM node:12.18.1 AS npm-build
|
||||
# --- Clone the kubeflow/kubeflow code ---
|
||||
FROM ubuntu AS fetch-kubeflow-kubeflow
|
||||
|
||||
# Build frontend.
|
||||
ADD /pkg/ui/v1beta1/frontend /frontend
|
||||
RUN cd /frontend && npm ci
|
||||
RUN cd /frontend && npm run build
|
||||
RUN rm -rf /frontend/node_modules
|
||||
RUN apt-get update && apt-get install git -y
|
||||
|
||||
# Build backend.
|
||||
WORKDIR /kf
|
||||
COPY ./pkg/ui/v1beta1/frontend/COMMIT ./
|
||||
RUN git clone https://github.com/kubeflow/kubeflow.git && \
|
||||
COMMIT=$(cat ./COMMIT) && \
|
||||
cd kubeflow && \
|
||||
git checkout $COMMIT
|
||||
|
||||
# --- Build the frontend kubeflow library ---
|
||||
FROM node:12 AS frontend-kubeflow-lib
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
ARG LIB=/kf/kubeflow/components/crud-web-apps/common/frontend/kubeflow-common-lib
|
||||
COPY --from=fetch-kubeflow-kubeflow $LIB/package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY --from=fetch-kubeflow-kubeflow $LIB/ ./
|
||||
RUN npm run build
|
||||
|
||||
# --- Build the frontend ---
|
||||
FROM node:12 AS frontend
|
||||
|
||||
WORKDIR /src
|
||||
COPY ./pkg/ui/v1beta1/frontend/package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY ./pkg/ui/v1beta1/frontend/ .
|
||||
COPY --from=frontend-kubeflow-lib /src/dist/kubeflow/ ./node_modules/kubeflow/
|
||||
|
||||
RUN npm run build:prod
|
||||
|
||||
# --- Build the backend ---
|
||||
FROM golang:alpine AS go-build
|
||||
|
||||
ARG TARGETARCH
|
||||
@@ -24,11 +51,11 @@ COPY cmd/ cmd/
|
||||
COPY pkg/ pkg/
|
||||
|
||||
# Build the binary.
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -a -o katib-ui ./cmd/ui/v1beta1
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -a -o katib-ui ./cmd/ui/v1beta1
|
||||
|
||||
# Copy the backend and frontend into a thin image.
|
||||
# --- Compose the web app ---
|
||||
FROM alpine:3.15
|
||||
WORKDIR /app
|
||||
COPY --from=go-build /go/src/github.com/kubeflow/katib/katib-ui /app/
|
||||
COPY --from=npm-build /frontend/build /app/build
|
||||
COPY --from=frontend /src/dist/static /app/build/static/
|
||||
ENTRYPOINT ["./katib-ui"]
|
||||
|
||||
@@ -33,7 +33,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
port = flag.String("port", "80", "The port to listen to for incoming HTTP connections")
|
||||
port = flag.String("port", "8080", "The port to listen to for incoming HTTP connections")
|
||||
host = flag.String("host", "0.0.0.0", "The host to listen to for incoming HTTP connections")
|
||||
buildDir = flag.String("build-dir", "/app/build", "The dir of frontend")
|
||||
dbManagerAddr = flag.String("db-manager-address", common_v1beta1.GetDBManagerAddr(), "The address of Katib DB manager")
|
||||
@@ -45,17 +45,17 @@ func main() {
|
||||
|
||||
log.Printf("Serving the frontend dir %s", *buildDir)
|
||||
frontend := http.FileServer(http.Dir(*buildDir))
|
||||
http.Handle("/katib/", http.StripPrefix("/katib/", frontend))
|
||||
http.HandleFunc("/katib/", kuh.ServeIndex(*buildDir))
|
||||
http.Handle("/katib/static/", http.StripPrefix("/katib/", frontend))
|
||||
|
||||
http.HandleFunc("/katib/fetch_experiments/", kuh.FetchAllExperiments)
|
||||
http.HandleFunc("/katib/fetch_experiments/", kuh.FetchExperiments)
|
||||
|
||||
http.HandleFunc("/katib/submit_yaml/", kuh.SubmitYamlJob)
|
||||
http.HandleFunc("/katib/submit_hp_job/", kuh.SubmitParamsJob)
|
||||
http.HandleFunc("/katib/submit_nas_job/", kuh.SubmitParamsJob)
|
||||
http.HandleFunc("/katib/create_experiment/", kuh.CreateExperiment)
|
||||
|
||||
http.HandleFunc("/katib/delete_experiment/", kuh.DeleteExperiment)
|
||||
|
||||
http.HandleFunc("/katib/fetch_experiment/", kuh.FetchExperiment)
|
||||
http.HandleFunc("/katib/fetch_trial/", kuh.FetchTrial)
|
||||
http.HandleFunc("/katib/fetch_suggestion/", kuh.FetchSuggestion)
|
||||
|
||||
http.HandleFunc("/katib/fetch_hp_job_info/", kuh.FetchHPJobInfo)
|
||||
@@ -67,6 +67,7 @@ func main() {
|
||||
http.HandleFunc("/katib/edit_template/", kuh.EditTemplate)
|
||||
http.HandleFunc("/katib/delete_template/", kuh.DeleteTemplate)
|
||||
http.HandleFunc("/katib/fetch_namespaces", kuh.FetchNamespaces)
|
||||
http.HandleFunc("/katib/fetch_trial_logs/", kuh.FetchTrialLogs)
|
||||
|
||||
log.Printf("Serving at %s:%s", *host, *port)
|
||||
if err := http.ListenAndServe(fmt.Sprintf("%s:%s", *host, *port), nil); err != nil {
|
||||
|
||||
@@ -132,7 +132,7 @@ Please see [new-algorithm-service.md](./new-algorithm-service.md).
|
||||
|
||||
## Katib UI documentation
|
||||
|
||||
Please see [Katib UI README](https://github.com/kubeflow/katib/tree/master/pkg/ui/v1beta1).
|
||||
Please see [Katib UI README](../pkg/ui/v1beta1).
|
||||
|
||||
## Design proposals
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/awalterschulze/gographviz v2.0.3+incompatible
|
||||
github.com/c-bata/goptuna v0.8.0
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/protobuf v1.5.2
|
||||
|
||||
@@ -422,8 +422,6 @@ github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASx
|
||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
# Katib User Interface
|
||||
|
||||
This is the source code for the Katib UI. Current version of Katib UI is v1beta1. On the official Kubeflow website [here](https://www.kubeflow.org/docs/components/katib/experiment/#running-the-experiment-from-the-katib-ui) you can find information how to use Katib UI.
|
||||
We are using [Angular](https://angular.io/) framework to create frontend and Go as a backend.
|
||||
|
||||
We are using [Material UI](https://material.angular.io/) to design frontend. Try to use Material UI components to implement new Katib UI features.
|
||||
|
||||
## Folder structure
|
||||
|
||||
1. You can find `Dockerfile` and `main.go` - file to serve the UI under `cmd/new-ui/v1beta1`
|
||||
|
||||
1. You can find Go backend under `pkg/new-ui/v1beta1`
|
||||
|
||||
1. You can find Angular frontend under `pkg/new-ui/v1beta1/frontend`
|
||||
|
||||
## Requirements
|
||||
|
||||
To make changes to the UI you need to install:
|
||||
|
||||
- Tools, defined [here](https://github.com/kubeflow/katib/blob/master/docs/developer-guide.md#requirements).
|
||||
|
||||
- `node` (v12.18.1) and `npm` (v6.13). Recommended to install `node` and `npm` using [`nvm`](https://github.com/nvm-sh/nvm). After installing `nvm`, you can run `nvm install 12.18.1` to install `node` version 12.18.1 and run `nvm use 12.18.1` to use that version.
|
||||
|
||||
## Development
|
||||
|
||||
While development you have different ways to run Katib UI.
|
||||
|
||||
1. Build and serve only the frontend. The dev server will also be proxying requests to the backend
|
||||
2. Build the frontend and serve it via the backend locally
|
||||
|
||||
### Serve only the frontend
|
||||
|
||||
You can run a webpack dev server that only exposes the frontend files, which can be useful for testing only the UI of the app. There's also a `proxy.conf.json` file which configures the dev server to send the backend requests to port `8000`.
|
||||
|
||||
In order to build the UI locally, and expose it with a webpack dev server you will need to:
|
||||
|
||||
1. Create a module from the [common library](https://github.com/kubeflow/kubeflow/tree/master/components/crud-web-apps/common/frontend/kubeflow-common-lib)
|
||||
2. Install the node modules of the app and also link the common-library module
|
||||
|
||||
You can build the common library with:
|
||||
|
||||
```bash
|
||||
COMMIT=$(cat ./frontend/COMMIT) \
|
||||
&& cd /tmp && git clone https://github.com/kubeflow/kubeflow.git \
|
||||
&& cd kubeflow \
|
||||
&& git checkout $COMMIT \
|
||||
&& cd components/crud-web-apps/common/frontend/kubeflow-common-lib
|
||||
|
||||
# build the common library module
|
||||
npm i
|
||||
npm run build
|
||||
|
||||
# link the module to your npm packages
|
||||
# depending on where you npm stores the global packages you
|
||||
# might need to use sudo
|
||||
npm link dist/kubeflow
|
||||
```
|
||||
|
||||
And then build and run the UI locally, on `localhost:4200`, with:
|
||||
|
||||
```bash
|
||||
# If you've already cloned the repo then skip this step and just
|
||||
# navigate to the pkg/new-ui/v1beta1/frontend dir
|
||||
cd /tmp && git clone https://github.com/kubeflow/katib.git \
|
||||
&& cd katib/pkg/new-ui/v1beta1/frontend
|
||||
|
||||
npm i
|
||||
npm link kubeflow
|
||||
npm run start
|
||||
```
|
||||
|
||||
### Serve the UI from the backend
|
||||
|
||||
This is the recommended way to test the web app e2e. In order to build the UI and serve it via the backend, locally, you will need to:
|
||||
|
||||
1. Build the UI locally. You have to follow the steps from the previous section, but instead of running `npm run start` you need to run `npm run build:prod`. It builds the frontend artifacts under `frontend/dist/static` folder.
|
||||
|
||||
Moreover, you are able to run `npm run build:watch` instead of `npm run build:prod`. In that case, it starts a process which is watching the source code changes and building the frontend artifacts under `frontend/dist/static` folder.
|
||||
|
||||
Learn more about Angular scripts in the [official guide](https://angular.io/cli/build).
|
||||
|
||||
1. Run `kubectl port-forward svc/katib-db-manager 6789 -n kubeflow` to expose `katib-db-manager` service for external access. You can use [different ways](https://kubernetes.io/docs/tasks/access-application-cluster/) to get external address for Kubernetes service. After exposing service, you should be able to receive information by running `wget <external-ip>:<service-port>`. In case of port-forwarding above, you have to run `wget localhost:6789`.
|
||||
|
||||
1. Go to `cmd/new-ui/v1beta1`.
|
||||
|
||||
1. Run `main.go` file with appropriate flags, where:
|
||||
|
||||
- `--build-dir` - directory with the frontend artifacts.
|
||||
- `--port` - port to access Katib UI.
|
||||
- `--db-manager-address` - Katib DB manager external IP and port address.
|
||||
|
||||
For example, if you use port-forwarding to expose `katib-db-manager`, run this command:
|
||||
|
||||
```
|
||||
export APP_DISABLE_AUTH=true
|
||||
go run main.go --build-dir=../../../pkg/new-ui/v1beta1/frontend/dist --port=8080 --db-manager-address=localhost:6789
|
||||
```
|
||||
|
||||
After that, you can access the UI using this URL: `http://localhost:8080/katib/`.
|
||||
|
||||
## Production
|
||||
|
||||
To run Katib UI in Production, after all changes in frontend and backend, you need to create an image for the UI. Under `/katib` directory run this: `docker build . -f cmd/new-ui/v1beta1/Dockerfile -t <name of your image>` to build the image.
|
||||
|
||||
After that, you can modify UI [deployment](https://github.com/kubeflow/katib/blob/master/manifests/v1beta1/components/ui/ui.yaml#L21) with your new image. Then, follow [these steps](https://www.kubeflow.org/docs/components/katib/hyperparameter/#accessing-the-katib-ui) to access Katib UI.
|
||||
|
||||
## Code style
|
||||
|
||||
To make frontend code consistent and easy to review we use [Prettier](https://prettier.io/). You can find Prettier config [here](https://github.com/kubeflow/katib/tree/master/pkg/new-ui/v1beta1/frontend/.prettierrc.yaml).
|
||||
Check [here](https://prettier.io/docs/en/install.html), how to install Prettier CLI to check and format your code.
|
||||
|
||||
### IDE integration
|
||||
|
||||
For VSCode you can install plugin: "Prettier - Code formatter" and it picks Prettier config automatically.
|
||||
|
||||
You can edit [settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settings-file-locations) file for VSCode to autoformat on save.
|
||||
|
||||
```json
|
||||
"settings": {
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
```
|
||||
|
||||
For others IDE see [this](https://prettier.io/docs/en/editors.html).
|
||||
|
||||
### Check and format code
|
||||
|
||||
Before submitting PR check and format your code. To check your code run `npm run format:check` under `/frontend` folder. To format your code run `npm run format:write` under `/frontend` folder.
|
||||
If all files formatted you can submit the PR.
|
||||
|
||||
If you don't want to format some code, [here](https://prettier.io/docs/en/ignore.html) is an instruction how to disable Prettier.
|
||||
@@ -1,772 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The Kubeflow Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
experimentv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1beta1"
|
||||
suggestionv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/suggestions/v1beta1"
|
||||
trialsv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1beta1"
|
||||
api_pb_v1beta1 "github.com/kubeflow/katib/pkg/apis/manager/v1beta1"
|
||||
consts "github.com/kubeflow/katib/pkg/controller.v1beta1/consts"
|
||||
"github.com/kubeflow/katib/pkg/util/v1beta1/katibclient"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
common "github.com/kubeflow/katib/pkg/apis/controller/common/v1beta1"
|
||||
mccommon "github.com/kubeflow/katib/pkg/metricscollector/v1beta1/common"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
func NewKatibUIHandler(dbManagerAddr string) *KatibUIHandler {
|
||||
kclient, err := katibclient.NewClient(client.Options{})
|
||||
if err != nil {
|
||||
log.Printf("NewClient for Katib failed: %v", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &KatibUIHandler{
|
||||
katibClient: kclient,
|
||||
dbManagerAddr: dbManagerAddr,
|
||||
}
|
||||
}
|
||||
|
||||
// ServeIndex will return index.html for any non-API URL
|
||||
func (k *KatibUIHandler) ServeIndex(buildDir string) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
fp := filepath.Join(buildDir, "static/index.html")
|
||||
log.Printf("Sending file %s for url: %s", fp, r.URL)
|
||||
|
||||
// never cache index.html
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0")
|
||||
|
||||
// return the contents of index.html
|
||||
http.ServeFile(w, r, fp)
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) connectManager() (*grpc.ClientConn, api_pb_v1beta1.DBManagerClient) {
|
||||
conn, err := grpc.Dial(k.dbManagerAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
log.Printf("Dial to GRPC failed: %v", err)
|
||||
return nil, nil
|
||||
}
|
||||
c := api_pb_v1beta1.NewDBManagerClient(conn)
|
||||
return conn, c
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) CreateExperiment(w http.ResponseWriter, r *http.Request) {
|
||||
var data map[string]interface{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
log.Printf("Failed to decode body: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
dataJSON, ok := data["postData"]
|
||||
if !ok {
|
||||
msg := "Couldn't load the 'postData' field of the request's data"
|
||||
log.Println(msg)
|
||||
http.Error(w, msg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
jsonbody, err := json.Marshal(dataJSON)
|
||||
if err != nil {
|
||||
log.Printf("Marshal data for experiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
job := experimentv1beta1.Experiment{}
|
||||
if err := json.Unmarshal(jsonbody, &job); err != nil {
|
||||
log.Printf("Unmarshal experiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
namespace := job.ObjectMeta.Namespace
|
||||
experimentName := job.ObjectMeta.Name
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeCreate, namespace, consts.PluralExperiment, "", experimentName, experimentv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to create experiment: %s in namespace: %s \n", user, experimentName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
err = k.katibClient.CreateRuntimeObject(&job)
|
||||
if err != nil {
|
||||
log.Printf("CreateRuntimeObject from parameters failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) FetchExperiments(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No 'namespace' query parameter was provided.")
|
||||
err := errors.New("no 'namespace' query parameter was provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeList, namespace, consts.PluralExperiment, "", "", experimentv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to list experiments in namespace: %s \n", user, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
experiments, err := k.getExperiments([]string{namespace})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response, err := json.Marshal(experiments)
|
||||
if err != nil {
|
||||
log.Printf("Marshal experiments failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write experiments failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) DeleteExperiment(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No 'namespace' query parameter was provided.")
|
||||
err := errors.New("no 'namespace' query parameter was provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
experimentNames, ok := r.URL.Query()["experimentName"]
|
||||
if !ok {
|
||||
log.Printf("No experimentName provided in Query parameteres! Provide an 'experimentName' param")
|
||||
err := errors.New("no experimentName provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
experimentName := experimentNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeDelete, namespace, consts.PluralExperiment, "", experimentName, experimentv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to delete experiment: %s in namespace: %s \n", user, experimentName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
experiment, err := k.katibClient.GetExperiment(experimentName, namespace)
|
||||
if err != nil {
|
||||
log.Printf("GetExperiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = k.katibClient.DeleteRuntimeObject(experiment)
|
||||
if err != nil {
|
||||
log.Printf("DeleteRuntimeObject failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
isExperimentDeleted := false
|
||||
|
||||
var experiments []ExperimentView
|
||||
|
||||
// Waiting until experiment will be deleted
|
||||
for !isExperimentDeleted {
|
||||
// At first, try to list experiments in cluster scope
|
||||
experiments, err = k.getExperiments([]string{""})
|
||||
if err != nil {
|
||||
// If failed, just try to list experiments from own namespace
|
||||
experiments, err = k.getExperiments([]string{})
|
||||
}
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
isExperimentDeleted = true
|
||||
for _, experiment := range experiments {
|
||||
if experiment.Name == experimentName {
|
||||
isExperimentDeleted = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response, err := json.Marshal(experiments)
|
||||
if err != nil {
|
||||
log.Printf("Marshal HP and NAS experiments failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write HP and NAS experiments failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FetchTrialTemplates gets all trial templates in all namespaces
|
||||
func (k *KatibUIHandler) FetchTrialTemplates(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
trialTemplatesViewList, err := k.getTrialTemplatesViewList(r)
|
||||
if err != nil {
|
||||
log.Printf("getTrialTemplatesViewList failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
TrialTemplatesResponse := TrialTemplatesResponse{
|
||||
Data: trialTemplatesViewList,
|
||||
}
|
||||
response, err := json.Marshal(TrialTemplatesResponse)
|
||||
if err != nil {
|
||||
log.Printf("Marshal templates failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write template failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// AddTemplate adds template to ConfigMap
|
||||
func (k *KatibUIHandler) AddTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var data map[string]interface{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
log.Printf("Failed to decode body: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
updatedConfigMapNamespace := data["updatedConfigMapNamespace"].(string)
|
||||
updatedConfigMapName := data["updatedConfigMapName"].(string)
|
||||
updatedConfigMapPath := data["updatedConfigMapPath"].(string)
|
||||
updatedTemplateYaml := data["updatedTemplateYaml"].(string)
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeCreate, updatedConfigMapNamespace, corev1.ResourceConfigMaps.String(), "", updatedConfigMapName, corev1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to add configmap: %s in namespace: %s \n", user, updatedConfigMapName, updatedConfigMapNamespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, "", updatedConfigMapPath, updatedTemplateYaml, ActionTypeAdd, r)
|
||||
if err != nil {
|
||||
log.Printf("updateTrialTemplates failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
TrialTemplatesResponse := TrialTemplatesResponse{
|
||||
Data: newTemplates,
|
||||
}
|
||||
response, err := json.Marshal(TrialTemplatesResponse)
|
||||
if err != nil {
|
||||
log.Printf("Marhal failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// EditTemplate edits template in ConfigMap
|
||||
func (k *KatibUIHandler) EditTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var data map[string]interface{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
log.Printf("Failed to decode body: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
updatedConfigMapNamespace := data["updatedConfigMapNamespace"].(string)
|
||||
updatedConfigMapName := data["updatedConfigMapName"].(string)
|
||||
configMapPath := data["configMapPath"].(string)
|
||||
updatedConfigMapPath := data["updatedConfigMapPath"].(string)
|
||||
updatedTemplateYaml := data["updatedTemplateYaml"].(string)
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeUpdate, updatedConfigMapNamespace, corev1.ResourceConfigMaps.String(), "", updatedConfigMapName, corev1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to edit configmap: %s in namespace: %s \n", user, updatedConfigMapName, updatedConfigMapNamespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, configMapPath, updatedConfigMapPath, updatedTemplateYaml, ActionTypeEdit, r)
|
||||
if err != nil {
|
||||
log.Printf("updateTrialTemplates failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
TrialTemplatesResponse := TrialTemplatesResponse{
|
||||
Data: newTemplates,
|
||||
}
|
||||
response, err := json.Marshal(TrialTemplatesResponse)
|
||||
if err != nil {
|
||||
log.Printf("Marhal failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteTemplate deletes template in ConfigMap
|
||||
func (k *KatibUIHandler) DeleteTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var data map[string]interface{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
log.Printf("Failed to decode body: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
updatedConfigMapNamespace := data["updatedConfigMapNamespace"].(string)
|
||||
updatedConfigMapName := data["updatedConfigMapName"].(string)
|
||||
updatedConfigMapPath := data["updatedConfigMapPath"].(string)
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeDelete, updatedConfigMapNamespace, corev1.ResourceConfigMaps.String(), "", updatedConfigMapName, corev1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to delete configmap: %s in namespace: %s \n", user, updatedConfigMapName, updatedConfigMapNamespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, "", updatedConfigMapPath, "", ActionTypeDelete, r)
|
||||
if err != nil {
|
||||
log.Printf("updateTrialTemplates failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
TrialTemplatesResponse := TrialTemplatesResponse{
|
||||
Data: newTemplates,
|
||||
}
|
||||
|
||||
response, err := json.Marshal(TrialTemplatesResponse)
|
||||
if err != nil {
|
||||
log.Printf("Marhal failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) FetchNamespaces(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Get all available namespaces
|
||||
namespaces, err := k.getAvailableNamespaces()
|
||||
if err != nil {
|
||||
log.Printf("GetAvailableNamespaces failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := json.Marshal(namespaces)
|
||||
if err != nil {
|
||||
log.Printf("Marshal namespaces failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write namespaces failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FetchExperiment gets experiment in specific namespace.
|
||||
func (k *KatibUIHandler) FetchExperiment(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No 'namespace' query parameter was provided.")
|
||||
err := errors.New("no 'namespace' query parameter was provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
experimentNames, ok := r.URL.Query()["experimentName"]
|
||||
if !ok {
|
||||
log.Printf("No experimentName provided in Query parameteres! Provide an 'experimentName' param")
|
||||
err := errors.New("no experimentName provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
experimentName := experimentNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, namespace, consts.PluralExperiment, "", experimentName, experimentv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get experiment: %s in namespace: %s \n", user, experimentName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
experiment, err := k.katibClient.GetExperiment(experimentName, namespace)
|
||||
if err != nil {
|
||||
log.Printf("GetExperiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response, err := json.Marshal(experiment)
|
||||
if err != nil {
|
||||
log.Printf("Marshal Experiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write Experiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FetchSuggestion gets suggestion in specific namespace
|
||||
func (k *KatibUIHandler) FetchSuggestion(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No namespace provided in Query parameters! Provide a 'namespace' param")
|
||||
err := errors.New("no 'namespace' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
suggestionNames, ok := r.URL.Query()["suggestionName"]
|
||||
if !ok {
|
||||
log.Printf("No experimentName provided in Query parameteres! Provide an 'experimentName' param")
|
||||
err := errors.New("no experimentName provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
suggestionName := suggestionNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, namespace, consts.PluralSuggestion, "", suggestionName, suggestionv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get suggestion: %s in namespace: %s \n", user, suggestionName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
suggestion, err := k.katibClient.GetSuggestion(suggestionName, namespace)
|
||||
if err != nil {
|
||||
log.Printf("GetSuggestion failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response, err := json.Marshal(suggestion)
|
||||
if err != nil {
|
||||
log.Printf("Marshal Suggestion failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write Suggestion failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FetchTrial gets trial in specific namespace.
|
||||
func (k *KatibUIHandler) FetchTrial(w http.ResponseWriter, r *http.Request) {
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No namespace provided in Query parameters! Provide a 'namespace' param")
|
||||
err := errors.New("no 'namespace' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialNames, ok := r.URL.Query()["trialName"]
|
||||
if !ok {
|
||||
log.Printf("No trialName provided in Query parameters! Provide a 'trialName' param")
|
||||
err := errors.New("no 'trialName' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialName := trialNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, namespace, consts.PluralTrial, "", trialName, trialsv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get trial: %s from namespace: %s \n", user, trialName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
trial, err := k.katibClient.GetTrial(trialName, namespace)
|
||||
if err != nil {
|
||||
log.Printf("GetTrial failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response, err := json.Marshal(trial)
|
||||
if err != nil {
|
||||
log.Printf("Marshal Trial failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write trial failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FetchTrialLogs fetches logs for a trial in specific namespace.
|
||||
func (k *KatibUIHandler) FetchTrialLogs(w http.ResponseWriter, r *http.Request) {
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No namespace provided in Query parameters! Provide a 'namespace' param")
|
||||
err := errors.New("no 'namespace' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialNames, ok := r.URL.Query()["trialName"]
|
||||
if !ok {
|
||||
log.Printf("No trialName provided in Query parameters! Provide a 'trialName' param")
|
||||
err := errors.New("no 'trialName' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialName := trialNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, namespace, consts.PluralTrial, "", trialName, trialsv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get trial: %s in namespace: %s \n", user, trialName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
trial := &trialsv1beta1.Trial{}
|
||||
if err := k.katibClient.GetClient().Get(context.Background(), types.NamespacedName{Name: trialName, Namespace: namespace}, trial); err != nil {
|
||||
log.Printf("GetLogs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Use controller-runtime client instead of kubernetes client to get logs, once this is available
|
||||
clientset, err := createKubernetesClientset()
|
||||
if err != nil {
|
||||
log.Printf("GetLogs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
podName, err := fetchMasterPodName(clientset, trial)
|
||||
if err != nil {
|
||||
log.Printf("GetLogs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
user, err = IsAuthorized(consts.ActionTypeGet, namespace, corev1.ResourcePods.String(), "log", podName, corev1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get pod logs: %s in namespace: %s \n", user, podName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
podLogOpts := corev1.PodLogOptions{}
|
||||
podLogOpts.Container = trial.Spec.PrimaryContainerName
|
||||
if trial.Spec.MetricsCollector.Collector.Kind == common.StdOutCollector {
|
||||
podLogOpts.Container = mccommon.MetricLoggerCollectorContainerName
|
||||
}
|
||||
|
||||
logs, err := fetchPodLogs(clientset, namespace, podName, podLogOpts)
|
||||
if err != nil {
|
||||
log.Printf("GetLogs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response, err := json.Marshal(logs)
|
||||
if err != nil {
|
||||
log.Printf("Marshal logs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write logs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// createKubernetesClientset returns kubernetes clientset
|
||||
func createKubernetesClientset() (*kubernetes.Clientset, error) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clientset, nil
|
||||
}
|
||||
|
||||
// fetchMasterPodName returns name of the master pod for a trial
|
||||
func fetchMasterPodName(clientset *kubernetes.Clientset, trial *trialsv1beta1.Trial) (string, error) {
|
||||
selectionLabel := consts.LabelTrialName + "=" + trial.ObjectMeta.Name
|
||||
for primaryKey, primaryValue := range trial.Spec.PrimaryPodLabels {
|
||||
selectionLabel = selectionLabel + "," + primaryKey + "=" + primaryValue
|
||||
}
|
||||
|
||||
podList, err := clientset.CoreV1().Pods(trial.ObjectMeta.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: selectionLabel})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(podList.Items) == 0 {
|
||||
return "", errors.New(`Failed to find logs for this Trial. Make sure you've set "spec.trialTemplate.retain"
|
||||
field to "true" in the Experiment definition. If this error persists then the Pod's logs are not currently
|
||||
persisted in the cluster.`)
|
||||
}
|
||||
|
||||
// If Pod is Running or Succeeded Pod, return it.
|
||||
for _, pod := range podList.Items {
|
||||
if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodRunning {
|
||||
return pod.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, return the first Failed Pod.
|
||||
for _, pod := range podList.Items {
|
||||
if pod.Status.Phase == corev1.PodFailed {
|
||||
return pod.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, return error since Pod is in the Pending state.
|
||||
return "", errors.New("Failed to get logs for this Trial. Pod is in the Pending or Unknown state.")
|
||||
}
|
||||
|
||||
// fetchPodLogs returns logs of a pod for the given job name and namespace
|
||||
func fetchPodLogs(clientset *kubernetes.Clientset, namespace string, podName string, podLogOpts corev1.PodLogOptions) (string, error) {
|
||||
req := clientset.CoreV1().Pods(namespace).GetLogs(podName, &podLogOpts)
|
||||
podLogs, err := req.Stream(context.Background())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer podLogs.Close()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = io.Copy(buf, podLogs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
str := buf.String()
|
||||
|
||||
return str, nil
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Rok files
|
||||
**/browse-in-rok*
|
||||
|
||||
# Font files (automatically generating)
|
||||
/src/assets/fonts
|
||||
|
||||
# Cypress
|
||||
cypress/screenshots
|
||||
cypress/downloads
|
||||
@@ -1,5 +0,0 @@
|
||||
singleQuote: true
|
||||
printWidth: 80
|
||||
trailingComma: all
|
||||
tabWidth: 2
|
||||
arrowParens: avoid
|
||||
-15568
File diff suppressed because it is too large
Load Diff
@@ -1,84 +0,0 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "npm run copyLibAssets && ng serve --proxy-config proxy.conf.json",
|
||||
"build:watch": "npm run copyLibAssets && ng build --base-href /katib/ --deploy-url /katib/static/ --outputPath dist/static --watch",
|
||||
"build:prod": "npm run copyLibAssets && ng build --base-href /katib/ --deploy-url /katib/static/ --outputPath dist/static --configuration production",
|
||||
"copyLibAssets": "cp -r ./node_modules/kubeflow/assets/* ./src/assets/",
|
||||
"test": "ng test",
|
||||
"test:prod": "ng test --browsers=ChromeHeadless --watch=false",
|
||||
"lint-check": "ng lint",
|
||||
"lint": "ng lint --fix",
|
||||
"format:check": "prettier --check 'src/**/*.{jsx,js,ts,html,scss,css}' || node scripts/check-format-error.js",
|
||||
"format:write": "prettier --write 'src/**/*.{jsx,js,ts,html,scss,css}'",
|
||||
"ui-test": "cypress open . --e2e",
|
||||
"ui-test-ci": "cypress run . --e2e",
|
||||
"ui-test-ci-all": "cypress run . --e2e --browser=chrome && cypress run . --e2e --browser=firefox"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~12.2.7",
|
||||
"@angular/cdk": "^12.2.7",
|
||||
"@angular/cdk-experimental": "^12.2.7",
|
||||
"@angular/common": "~12.2.7",
|
||||
"@angular/compiler": "~12.2.7",
|
||||
"@angular/core": "~12.2.7",
|
||||
"@angular/forms": "~12.2.7",
|
||||
"@angular/localize": "^12.2.7",
|
||||
"@angular/material": "^12.2.7",
|
||||
"@angular/platform-browser": "~12.2.7",
|
||||
"@angular/platform-browser-dynamic": "~12.2.7",
|
||||
"@angular/router": "~12.2.7",
|
||||
"@fortawesome/angular-fontawesome": "^0.9.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.26",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.12.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.12.0",
|
||||
"@swimlane/ngx-charts": "^19.0.1",
|
||||
"date-fns": "1.29.0",
|
||||
"echarts": "^5.3.2",
|
||||
"echarts-stat": "^1.2.0",
|
||||
"js-yaml": "^4.0.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"material-icons": "^0.7.7",
|
||||
"monaco-editor": "^0.33.0",
|
||||
"ngx-echarts": "^8.0.1",
|
||||
"ngx-json-viewer": "^2.4.0",
|
||||
"rxjs": "~6.6.7",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~12.2.7",
|
||||
"@angular-eslint/builder": "12.7.0",
|
||||
"@angular-eslint/eslint-plugin": "12.7.0",
|
||||
"@angular-eslint/eslint-plugin-template": "12.7.0",
|
||||
"@angular-eslint/schematics": "12.7.0",
|
||||
"@angular-eslint/template-parser": "12.7.0",
|
||||
"@angular/cli": "~12.2.7",
|
||||
"@angular/compiler-cli": "~12.2.7",
|
||||
"@angular/language-service": "~12.2.7",
|
||||
"@cypress/schematic": "^2.3.0",
|
||||
"@kubernetes/client-node": "^0.12.2",
|
||||
"@types/jasmine": "~3.6.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"@typescript-eslint/eslint-plugin": "4.28.2",
|
||||
"@typescript-eslint/parser": "4.28.2",
|
||||
"codelyzer": "^6.0.0",
|
||||
"cypress": "^10.10.0",
|
||||
"eslint": "^7.26.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~6.3.16",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"prettier": "2.3.2",
|
||||
"ts-node": "~7.0.0",
|
||||
"typescript": "~4.3.5",
|
||||
"wait-on": "^7.0.1"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
console.error();
|
||||
console.error('-----------------------------------------------');
|
||||
console.error('Please run `npm run format:write` to format your code.');
|
||||
console.error('-----------------------------------------------');
|
||||
console.error();
|
||||
process.exit(1);
|
||||
@@ -1,320 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The Kubeflow Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
commonv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/common/v1beta1"
|
||||
experimentv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1beta1"
|
||||
trialv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1beta1"
|
||||
api_pb_v1beta1 "github.com/kubeflow/katib/pkg/apis/manager/v1beta1"
|
||||
"github.com/kubeflow/katib/pkg/controller.v1beta1/consts"
|
||||
)
|
||||
|
||||
const kfpRunIDAnnotation = "kubeflow-kale.org/kfp-run-uuid"
|
||||
|
||||
func (k *KatibUIHandler) FetchHPJobInfo(w http.ResponseWriter, r *http.Request) {
|
||||
//enableCors(&w)
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No namespace provided in Query parameters! Provide a 'namespace' param")
|
||||
err := errors.New("no 'namespace' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
experimentNames, ok := r.URL.Query()["experimentName"]
|
||||
if !ok {
|
||||
log.Printf("No experimentName provided in Query parameteres! Provide an 'experimentName' param")
|
||||
err := errors.New("no experimentName provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
experimentName := experimentNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, namespace, consts.PluralExperiment, "", experimentName, experimentv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get experiments from namespace: %s \n", user, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Start FetchHPJobInfo for Experiment: %v in namespace: %v", experimentName, namespace)
|
||||
|
||||
conn, c := k.connectManager()
|
||||
defer conn.Close()
|
||||
|
||||
resultText := "Status,trialName"
|
||||
experiment, err := k.katibClient.GetExperiment(experimentName, namespace)
|
||||
if err != nil {
|
||||
log.Printf("GetExperiment from HP job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Printf("Got Experiment %v", experimentName)
|
||||
metricsList := map[string]int{}
|
||||
metricsName := experiment.Spec.Objective.ObjectiveMetricName
|
||||
resultText += "," + metricsName
|
||||
metricsList[metricsName] = 0
|
||||
for i, m := range experiment.Spec.Objective.AdditionalMetricNames {
|
||||
resultText += "," + m
|
||||
metricsList[m] = i + 1
|
||||
}
|
||||
log.Printf("Got metrics names")
|
||||
paramList := map[string]int{}
|
||||
for i, p := range experiment.Spec.Parameters {
|
||||
resultText += "," + p.Name
|
||||
paramList[p.Name] = i + len(metricsList)
|
||||
}
|
||||
log.Printf("Got Parameters names")
|
||||
|
||||
_, err = IsAuthorized(consts.ActionTypeList, namespace, consts.PluralTrial, "", "", trialv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to list trials from namespace: %s \n", user, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
trialList, err := k.katibClient.GetTrialList(experimentName, namespace)
|
||||
if err != nil {
|
||||
log.Printf("GetTrialList from HP job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Printf("Got Trial List - Count: %v", len(trialList.Items))
|
||||
|
||||
// append a column for the Pipeline UID associated with the Trial
|
||||
if havePipelineUID(trialList.Items) {
|
||||
resultText += ",KFP Run"
|
||||
}
|
||||
|
||||
foundPipelineUID := false
|
||||
for _, t := range trialList.Items {
|
||||
runUid, ok := t.GetAnnotations()[kfpRunIDAnnotation]
|
||||
if !ok {
|
||||
log.Printf("Trial %s has no pipeline run.", t.Name)
|
||||
runUid = ""
|
||||
} else {
|
||||
foundPipelineUID = true
|
||||
}
|
||||
|
||||
var lastTrialCondition string
|
||||
|
||||
// Take only the latest condition
|
||||
if len(t.Status.Conditions) > 0 {
|
||||
lastTrialCondition = string(t.Status.Conditions[len(t.Status.Conditions)-1].Type)
|
||||
}
|
||||
|
||||
trialResText := make([]string, len(metricsList)+len(paramList))
|
||||
|
||||
if t.IsSucceeded() || t.IsEarlyStopped() {
|
||||
obsLogResp, err := c.GetObservationLog(
|
||||
context.Background(),
|
||||
&api_pb_v1beta1.GetObservationLogRequest{
|
||||
TrialName: t.Name,
|
||||
StartTime: "",
|
||||
EndTime: "",
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("GetObservationLog from HP job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
for _, m := range obsLogResp.ObservationLog.MetricLogs {
|
||||
if trialResText[metricsList[m.Metric.Name]] == "" {
|
||||
trialResText[metricsList[m.Metric.Name]] = m.Metric.Value
|
||||
} else {
|
||||
currentValue, _ := strconv.ParseFloat(m.Metric.Value, 64)
|
||||
bestValue, _ := strconv.ParseFloat(trialResText[metricsList[m.Metric.Name]], 64)
|
||||
if t.Spec.Objective.Type == commonv1beta1.ObjectiveTypeMinimize && currentValue < bestValue {
|
||||
trialResText[metricsList[m.Metric.Name]] = m.Metric.Value
|
||||
} else if t.Spec.Objective.Type == commonv1beta1.ObjectiveTypeMaximize && currentValue > bestValue {
|
||||
trialResText[metricsList[m.Metric.Name]] = m.Metric.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, trialParam := range t.Spec.ParameterAssignments {
|
||||
trialResText[paramList[trialParam.Name]] = trialParam.Value
|
||||
}
|
||||
resultText += "\n" + lastTrialCondition + "," + t.Name + "," + strings.Join(trialResText, ",")
|
||||
if foundPipelineUID {
|
||||
resultText += "," + runUid
|
||||
}
|
||||
}
|
||||
log.Printf("Logs parsed, results:\n %v", resultText)
|
||||
response, err := json.Marshal(resultText)
|
||||
if err != nil {
|
||||
log.Printf("Marshal result text for HP job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write result text for HP job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FetchHPJobTrialInfo returns all metrics for the HP Job Trial
|
||||
func (k *KatibUIHandler) FetchHPJobTrialInfo(w http.ResponseWriter, r *http.Request) {
|
||||
//enableCors(&w)
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No namespace provided in Query parameters! Provide a 'namespace' param")
|
||||
err := errors.New("no 'namespace' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialNames, ok := r.URL.Query()["trialName"]
|
||||
if !ok {
|
||||
log.Printf("No trialName provided in Query parameters! Provide a 'trialName' param")
|
||||
err := errors.New("no 'trialName' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialName := trialNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
conn, c := k.connectManager()
|
||||
defer conn.Close()
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeList, namespace, consts.PluralTrial, "", trialName, trialv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get trial: %s from namespace: %s \n", user, trialName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
trial, err := k.katibClient.GetTrial(trialName, namespace)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("GetTrial from HP job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
objectiveType := trial.Spec.Objective.Type
|
||||
|
||||
// resultArray - array of arrays, where [i][0] - metricName, [i][1] - metricTime, [i][2] - metricValue
|
||||
var resultArray [][]string
|
||||
resultArray = append(resultArray, strings.Split("metricName,time,value", ","))
|
||||
obsLogResp, err := c.GetObservationLog(
|
||||
context.Background(),
|
||||
&api_pb_v1beta1.GetObservationLogRequest{
|
||||
TrialName: trialName,
|
||||
StartTime: "",
|
||||
EndTime: "",
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("GetObservationLog failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// prevMetricTimeValue is the dict, where key = metric name,
|
||||
// value = array, where [0] - Last metric time, [1] - Best metric value for this time
|
||||
prevMetricTimeValue := make(map[string][]string)
|
||||
for _, m := range obsLogResp.ObservationLog.MetricLogs {
|
||||
parsedCurrentTime, _ := time.Parse(time.RFC3339Nano, m.TimeStamp)
|
||||
formatCurrentTime := parsedCurrentTime.Format("2006-01-02T15:04:05")
|
||||
if _, found := prevMetricTimeValue[m.Metric.Name]; !found {
|
||||
prevMetricTimeValue[m.Metric.Name] = []string{"", ""}
|
||||
|
||||
}
|
||||
|
||||
newMetricValue, err := strconv.ParseFloat(m.Metric.Value, 64)
|
||||
if err != nil {
|
||||
log.Printf("ParseFloat for new metric value: %v failed: %v", m.Metric.Value, err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var prevMetricValue float64
|
||||
if prevMetricTimeValue[m.Metric.Name][1] != "" {
|
||||
prevMetricValue, err = strconv.ParseFloat(prevMetricTimeValue[m.Metric.Name][1], 64)
|
||||
if err != nil {
|
||||
log.Printf("ParseFloat for prev metric value: %v failed: %v", prevMetricTimeValue[m.Metric.Name][1], err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if formatCurrentTime == prevMetricTimeValue[m.Metric.Name][0] &&
|
||||
((objectiveType == commonv1beta1.ObjectiveTypeMinimize &&
|
||||
newMetricValue < prevMetricValue) ||
|
||||
(objectiveType == commonv1beta1.ObjectiveTypeMaximize &&
|
||||
newMetricValue > prevMetricValue)) {
|
||||
|
||||
prevMetricTimeValue[m.Metric.Name][1] = m.Metric.Value
|
||||
for i := len(resultArray) - 1; i >= 0; i-- {
|
||||
if resultArray[i][0] == m.Metric.Name {
|
||||
resultArray[i][2] = m.Metric.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if formatCurrentTime != prevMetricTimeValue[m.Metric.Name][0] {
|
||||
resultArray = append(resultArray, []string{m.Metric.Name, formatCurrentTime, m.Metric.Value})
|
||||
prevMetricTimeValue[m.Metric.Name][0] = formatCurrentTime
|
||||
prevMetricTimeValue[m.Metric.Name][1] = m.Metric.Value
|
||||
}
|
||||
}
|
||||
|
||||
var resultText string
|
||||
for _, metric := range resultArray {
|
||||
resultText += strings.Join(metric, ",") + "\n"
|
||||
}
|
||||
|
||||
response, err := json.Marshal(resultText)
|
||||
if err != nil {
|
||||
log.Printf("Marshal result text in Trial info failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write result text in Trial info failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The Kubeflow Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
trialsv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1beta1"
|
||||
api_pb_v1beta1 "github.com/kubeflow/katib/pkg/apis/manager/v1beta1"
|
||||
)
|
||||
|
||||
func (k *KatibUIHandler) FetchNASJobInfo(w http.ResponseWriter, r *http.Request) {
|
||||
//enableCors(&w)
|
||||
experimentName := r.URL.Query()["experimentName"][0]
|
||||
namespace := r.URL.Query()["namespace"][0]
|
||||
|
||||
responseRaw := make([]NNView, 0)
|
||||
var architecture string
|
||||
var decoder string
|
||||
|
||||
conn, c := k.connectManager()
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
trials, err := k.katibClient.GetTrialList(experimentName, namespace)
|
||||
if err != nil {
|
||||
log.Printf("GetTrialList from NAS job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
log.Printf("Got Trial List")
|
||||
|
||||
for i, t := range trials.Items {
|
||||
succeeded := false
|
||||
for _, condition := range t.Status.Conditions {
|
||||
if condition.Type == trialsv1beta1.TrialSucceeded {
|
||||
succeeded = true
|
||||
}
|
||||
}
|
||||
if succeeded {
|
||||
obsLogResp, err := c.GetObservationLog(
|
||||
context.Background(),
|
||||
&api_pb_v1beta1.GetObservationLogRequest{
|
||||
TrialName: t.Name,
|
||||
StartTime: "",
|
||||
EndTime: "",
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("GetObservationLog from NAS job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
metricsName := make([]string, 0)
|
||||
metricsValue := make([]string, 0)
|
||||
for _, m := range obsLogResp.ObservationLog.MetricLogs {
|
||||
metricsName = append(metricsName, m.Metric.Name)
|
||||
metricsValue = append(metricsValue, m.Metric.Value)
|
||||
|
||||
}
|
||||
for _, trialParam := range t.Spec.ParameterAssignments {
|
||||
if trialParam.Name == "architecture" {
|
||||
architecture = trialParam.Value
|
||||
}
|
||||
if trialParam.Name == "nn_config" {
|
||||
decoder = trialParam.Value
|
||||
}
|
||||
}
|
||||
responseRaw = append(responseRaw, NNView{
|
||||
Name: "Generation " + strconv.Itoa(i),
|
||||
TrialName: t.Name,
|
||||
Architecture: generateNNImage(architecture, decoder),
|
||||
MetricsName: metricsName,
|
||||
MetricsValue: metricsValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
log.Printf("Logs parsed, result: %v", responseRaw)
|
||||
|
||||
response, err := json.Marshal(responseRaw)
|
||||
if err != nil {
|
||||
log.Printf("Marshal result in NAS job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write result in NAS job failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The Kubeflow Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
v1beta1experiment "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1beta1"
|
||||
"github.com/kubeflow/katib/pkg/controller.v1beta1/consts"
|
||||
"github.com/kubeflow/katib/pkg/util/v1beta1/katibclient"
|
||||
)
|
||||
|
||||
var (
|
||||
TrialTemplateLabel = map[string]string{
|
||||
consts.LabelTrialTemplateConfigMapName: consts.LabelTrialTemplateConfigMapValue}
|
||||
)
|
||||
|
||||
type Decoder struct {
|
||||
Layers int `json:"num_layers"`
|
||||
InputSize []int `json:"input_size"`
|
||||
OutputSize []int `json:"output_size"`
|
||||
Embedding map[int]*Block `json:"embedding"`
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
ID int `json:"opt_id"`
|
||||
Type string `json:"opt_type"`
|
||||
Param Option `json:"opt_params"`
|
||||
}
|
||||
|
||||
type Option struct {
|
||||
FilterNumber string `json:"num_filter"`
|
||||
FilterSize string `json:"filter_size"`
|
||||
Stride string `json:"stride"`
|
||||
}
|
||||
|
||||
type ExperimentView struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
Type string `json:"type"`
|
||||
Status string `json:"status"`
|
||||
v1beta1experiment.ExperimentStatus
|
||||
}
|
||||
|
||||
type TrialTemplatesDataView struct {
|
||||
ConfigMapNamespace string
|
||||
ConfigMaps []ConfigMap
|
||||
}
|
||||
|
||||
type TrialTemplatesResponse struct {
|
||||
Data []TrialTemplatesDataView
|
||||
}
|
||||
|
||||
type ConfigMap struct {
|
||||
ConfigMapName string
|
||||
Templates []Template
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
Path string
|
||||
Yaml string
|
||||
}
|
||||
|
||||
type KatibUIHandler struct {
|
||||
katibClient katibclient.Client
|
||||
dbManagerAddr string
|
||||
}
|
||||
|
||||
type NNView struct {
|
||||
Name string
|
||||
TrialName string
|
||||
Architecture string
|
||||
MetricsName []string
|
||||
MetricsValue []string
|
||||
}
|
||||
|
||||
type JobType string
|
||||
|
||||
const (
|
||||
ExperimentTypeHP = "hp"
|
||||
ExperimentTypeNAS = "nas"
|
||||
ActionTypeAdd = "add"
|
||||
ActionTypeEdit = "edit"
|
||||
ActionTypeDelete = "delete"
|
||||
)
|
||||
@@ -1,338 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The Kubeflow Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/kubeflow/katib/pkg/controller.v1beta1/consts"
|
||||
|
||||
gographviz "github.com/awalterschulze/gographviz"
|
||||
trialv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1beta1"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (k *KatibUIHandler) getExperiments(namespace []string) ([]ExperimentView, error) {
|
||||
experiments := []ExperimentView{}
|
||||
|
||||
el, err := k.katibClient.GetExperimentList(namespace...)
|
||||
if err != nil {
|
||||
log.Printf("GetExperimentList failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
for _, experiment := range el.Items {
|
||||
experimentLastCondition, err := experiment.GetLastConditionType()
|
||||
if err != nil {
|
||||
log.Printf("GetLastConditionType failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
tp := ExperimentTypeHP
|
||||
if experiment.Spec.NasConfig != nil {
|
||||
tp = ExperimentTypeNAS
|
||||
}
|
||||
|
||||
newExperiment := ExperimentView{
|
||||
experiment.Name,
|
||||
experiment.Namespace,
|
||||
tp,
|
||||
string(experimentLastCondition),
|
||||
experiment.Status,
|
||||
}
|
||||
|
||||
experiments = append(experiments, newExperiment)
|
||||
}
|
||||
|
||||
return experiments, nil
|
||||
}
|
||||
|
||||
func havePipelineUID(trials []trialv1beta1.Trial) bool {
|
||||
for _, t := range trials {
|
||||
_, ok := t.GetAnnotations()[kfpRunIDAnnotation]
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) getTrialTemplatesViewList(r *http.Request) ([]TrialTemplatesDataView, error) {
|
||||
trialTemplatesDataView := make([]TrialTemplatesDataView, 0)
|
||||
|
||||
// Get all available namespaces
|
||||
namespaces, err := k.getAvailableNamespaces()
|
||||
if err != nil {
|
||||
log.Printf("GetAvailableNamespaces failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get Trial Template ConfigMap for each namespace
|
||||
for _, ns := range namespaces {
|
||||
trialTemplatesConfigMapList, err := k.katibClient.GetTrialTemplates(ns)
|
||||
if err != nil {
|
||||
log.Printf("GetTrialTemplates failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Iterate over the trialTemplatesConfigMapList from all namespaces and filter out the
|
||||
// configmaps that belong to namespaces other than kubeflow and the ones that the user has
|
||||
// access privileges.
|
||||
var newTrialTemplatesConfigMapList []apiv1.ConfigMap
|
||||
for _, cmap := range trialTemplatesConfigMapList.Items {
|
||||
if ns == consts.DefaultKatibNamespace {
|
||||
// Add the configmaps from kubeflow namespace (no user can have access to kubeflow ns, so we hardcode it)
|
||||
newTrialTemplatesConfigMapList = append(newTrialTemplatesConfigMapList, cmap)
|
||||
} else {
|
||||
// for all other namespaces check authorization rbac
|
||||
configmapName := cmap.ObjectMeta.Name
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, ns, apiv1.ResourceConfigMaps.String(), "", configmapName, apiv1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if err != nil {
|
||||
log.Printf("The user: %s is not authorized to view configMap: %s in namespace: %s \n", user, configmapName, ns)
|
||||
return nil, err
|
||||
} else {
|
||||
log.Printf("The user: %s is authorized to view configMap: %s in namespace: %s", user, configmapName, ns)
|
||||
newTrialTemplatesConfigMapList = append(newTrialTemplatesConfigMapList, cmap)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(trialTemplatesConfigMapList.Items) != 0 {
|
||||
newTrialTemplatesView := getTrialTemplatesView(newTrialTemplatesConfigMapList)
|
||||
// ConfigMap with templates must exists in namespace
|
||||
if len(newTrialTemplatesView.ConfigMaps) > 0 {
|
||||
trialTemplatesDataView = append(trialTemplatesDataView, newTrialTemplatesView)
|
||||
}
|
||||
}
|
||||
}
|
||||
return trialTemplatesDataView, nil
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) getAvailableNamespaces() ([]string, error) {
|
||||
var namespaces []string
|
||||
|
||||
namespaceList, err := k.katibClient.GetNamespaceList()
|
||||
if err != nil {
|
||||
namespaces = append(namespaces, consts.DefaultKatibNamespace)
|
||||
return namespaces, nil
|
||||
}
|
||||
for _, ns := range namespaceList.Items {
|
||||
namespaces = append(namespaces, ns.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
return namespaces, nil
|
||||
}
|
||||
|
||||
func getTrialTemplatesView(templatesConfigMapList []apiv1.ConfigMap) TrialTemplatesDataView {
|
||||
|
||||
trialTemplatesDataView := TrialTemplatesDataView{
|
||||
ConfigMapNamespace: templatesConfigMapList[0].ObjectMeta.Namespace,
|
||||
ConfigMaps: []ConfigMap{},
|
||||
}
|
||||
for _, configMap := range templatesConfigMapList {
|
||||
newConfigMap := ConfigMap{
|
||||
ConfigMapName: configMap.ObjectMeta.Name,
|
||||
Templates: []Template{},
|
||||
}
|
||||
for key := range configMap.Data {
|
||||
newTemplate := Template{
|
||||
Path: key,
|
||||
Yaml: configMap.Data[key],
|
||||
}
|
||||
newConfigMap.Templates = append(newConfigMap.Templates, newTemplate)
|
||||
}
|
||||
|
||||
// Sort Trial Templates by Path
|
||||
sort.SliceStable(newConfigMap.Templates, func(i, j int) bool {
|
||||
return newConfigMap.Templates[i].Path <= newConfigMap.Templates[j].Path
|
||||
})
|
||||
|
||||
// Templates with data must exists in ConfigMap
|
||||
if len(newConfigMap.Templates) > 0 {
|
||||
trialTemplatesDataView.ConfigMaps = append(trialTemplatesDataView.ConfigMaps, newConfigMap)
|
||||
}
|
||||
|
||||
}
|
||||
return trialTemplatesDataView
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) updateTrialTemplates(
|
||||
updatedConfigMapNamespace,
|
||||
updatedConfigMapName,
|
||||
configMapPath,
|
||||
updatedConfigMapPath,
|
||||
updatedTemplateYaml,
|
||||
actionType string,
|
||||
r *http.Request) ([]TrialTemplatesDataView, error) {
|
||||
|
||||
templates, err := k.katibClient.GetConfigMap(updatedConfigMapName, updatedConfigMapNamespace)
|
||||
if err != nil && !(errors.IsNotFound(err) && actionType == ActionTypeAdd) {
|
||||
log.Printf("GetConfigMap failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if actionType == ActionTypeAdd {
|
||||
if len(templates) == 0 {
|
||||
templates = make(map[string]string)
|
||||
templates[updatedConfigMapPath] = updatedTemplateYaml
|
||||
} else {
|
||||
templates[updatedConfigMapPath] = updatedTemplateYaml
|
||||
}
|
||||
} else if actionType == ActionTypeEdit {
|
||||
delete(templates, configMapPath)
|
||||
templates[updatedConfigMapPath] = updatedTemplateYaml
|
||||
} else if actionType == ActionTypeDelete {
|
||||
delete(templates, updatedConfigMapPath)
|
||||
}
|
||||
|
||||
templatesConfigMap := &apiv1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: updatedConfigMapName,
|
||||
Namespace: updatedConfigMapNamespace,
|
||||
Labels: TrialTemplateLabel,
|
||||
},
|
||||
Data: templates,
|
||||
}
|
||||
|
||||
// If templates is empty delete Trial template configMap
|
||||
if len(templates) == 0 {
|
||||
err = k.katibClient.DeleteRuntimeObject(templatesConfigMap)
|
||||
if err != nil {
|
||||
log.Printf("DeleteRuntimeObject failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
// If len(templates) == 1 and adding template, we must create new ConfigMap
|
||||
} else if len(templates) == 1 && actionType == ActionTypeAdd {
|
||||
err = k.katibClient.CreateRuntimeObject(templatesConfigMap)
|
||||
if err != nil {
|
||||
log.Printf("CreateRuntimeObject failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
// Otherwise updating configMap
|
||||
} else {
|
||||
err = k.katibClient.UpdateRuntimeObject(templatesConfigMap)
|
||||
if err != nil {
|
||||
log.Printf("UpdateRuntimeObject failed: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
newTemplates, err := k.getTrialTemplatesViewList(r)
|
||||
if err != nil {
|
||||
log.Printf("getTrialTemplatesViewList: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newTemplates, nil
|
||||
|
||||
}
|
||||
func getNodeString(block *Block) string {
|
||||
var nodeString string
|
||||
switch block.Type {
|
||||
case "convolution":
|
||||
nodeString += block.Param.FilterSize + "x" + block.Param.FilterSize
|
||||
nodeString += " conv\n"
|
||||
nodeString += block.Param.FilterSize + " channels"
|
||||
case "separable_convolution":
|
||||
nodeString += block.Param.FilterSize + "x" + block.Param.FilterSize
|
||||
nodeString += " sep_conv\n"
|
||||
nodeString += block.Param.FilterSize + " channels"
|
||||
case "depthwise_convolution":
|
||||
nodeString += block.Param.FilterSize + "x" + block.Param.FilterSize
|
||||
nodeString += " depth_conv\n"
|
||||
case "reduction":
|
||||
// TODO: Need to be fixed
|
||||
nodeString += "3x3 max_pooling"
|
||||
}
|
||||
return strconv.Quote(nodeString)
|
||||
}
|
||||
|
||||
func generateNNImage(architecture string, decoder string) string {
|
||||
|
||||
var architectureInt [][]int
|
||||
|
||||
if err := json.Unmarshal([]byte(architecture), &architectureInt); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
/*
|
||||
Always has num_layers, input_size, output_size and embeding
|
||||
Embeding is a map: int to Parameter
|
||||
Parameter has id, type, Option
|
||||
|
||||
Beforehand substitute all ' to " and wrap the string in `
|
||||
*/
|
||||
|
||||
replacedDecoder := strings.Replace(decoder, `'`, `"`, -1)
|
||||
var decoderParsed Decoder
|
||||
|
||||
err := json.Unmarshal([]byte(replacedDecoder), &decoderParsed)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
graphAst, _ := gographviz.ParseString(`digraph G {}`)
|
||||
graph := gographviz.NewGraph()
|
||||
if err := gographviz.Analyse(graphAst, graph); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = graph.AddNode("G", "0", map[string]string{"label": strconv.Quote("Input")}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var i int
|
||||
for i = 0; i < len(architectureInt); i++ {
|
||||
if err = graph.AddNode("G", strconv.Itoa(i+1), map[string]string{"label": getNodeString(decoderParsed.Embedding[architectureInt[i][0]])}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = graph.AddEdge(strconv.Itoa(i), strconv.Itoa(i+1), true, nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for j := 1; j < i+1; j++ {
|
||||
if architectureInt[i][j] == 1 {
|
||||
if err = graph.AddEdge(strconv.Itoa(j-1), strconv.Itoa(i+1), true, nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err = graph.AddNode("G", strconv.Itoa(i+1), map[string]string{"label": strconv.Quote("GlobalAvgPool")}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = graph.AddEdge(strconv.Itoa(i), strconv.Itoa(i+1), true, nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = graph.AddNode("G", strconv.Itoa(i+2), map[string]string{"label": strconv.Quote("FullConnect\nSoftmax")}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = graph.AddEdge(strconv.Itoa(i+1), strconv.Itoa(i+2), true, nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = graph.AddNode("G", strconv.Itoa(i+3), map[string]string{"label": strconv.Quote("Output")}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = graph.AddEdge(strconv.Itoa(i+2), strconv.Itoa(i+3), true, nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s := graph.String()
|
||||
return s
|
||||
}
|
||||
+60
-24
@@ -1,17 +1,17 @@
|
||||
# Katib User Interface
|
||||
|
||||
This is the source code for the Katib UI. Current version of Katib UI is v1beta1. On the official Kubeflow website [here](https://www.kubeflow.org/docs/components/katib/experiment/#running-the-experiment-from-the-katib-ui) you can find information how to use Katib UI.
|
||||
We are using [React](https://reactjs.org/) framework to create frontend and Go as a backend.
|
||||
We are using [Angular](https://angular.io/) framework to create frontend and Go as a backend.
|
||||
|
||||
We are using [Material UI](https://material-ui.com/) to design frontend. Try to use Material UI components to implement new Katib UI features.
|
||||
We are using [Material UI](https://material.angular.io/) to design frontend. Try to use Material UI components to implement new Katib UI features.
|
||||
|
||||
## Folder structure
|
||||
|
||||
1. You can find `Dockerfile` and `main.go` - file to serve the UI under [cmd/ui/v1beta1](https://github.com/kubeflow/katib/tree/master/cmd/ui/v1beta1).
|
||||
1. You can find `Dockerfile` and `main.go` - file to serve the UI under [`cmd/ui/v1beta1`](../../../cmd/ui/v1beta1/)
|
||||
|
||||
1. You can find Go backend under [pkg/ui/v1beta1](https://github.com/kubeflow/katib/tree/master/pkg/ui/v1beta1).
|
||||
1. You can find Go backend under [`pkg/ui/v1beta1`](../../../pkg/ui/v1beta1)
|
||||
|
||||
1. You can find React frontend under [pkg/ui/v1beta1/frontend](https://github.com/kubeflow/katib/tree/master/pkg/ui/v1beta1/frontend).
|
||||
1. You can find Angular frontend under [`pkg/ui/v1beta1/frontend`](../../../pkg/ui/v1beta1/frontend/)
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -19,61 +19,97 @@ To make changes to the UI you need to install:
|
||||
|
||||
- Tools, defined [here](https://github.com/kubeflow/katib/blob/master/docs/developer-guide.md#requirements).
|
||||
|
||||
- `node` (v12 or later) and `npm` (v6.13 or later). Recommended to install `node` and `npm` using [`nvm`](https://github.com/nvm-sh/nvm). After installing `nvm`, you can run `nvm install 12.18.1` to install `node` version 12.18.1 and run `nvm use 12.18.1` to use that version.
|
||||
- `node` (v12.18.1) and `npm` (v6.13). Recommended to install `node` and `npm` using [`nvm`](https://github.com/nvm-sh/nvm). After installing `nvm`, you can run `nvm install 12.18.1` to install `node` version 12.18.1 and run `nvm use 12.18.1` to use that version.
|
||||
|
||||
## Development
|
||||
|
||||
While development you have different ways to run Katib UI.
|
||||
|
||||
### First time
|
||||
1. Build and serve only the frontend. The dev server will also be proxying requests to the backend
|
||||
2. Build the frontend and serve it via the backend locally
|
||||
|
||||
1. Clone the repository.
|
||||
### Serve only the frontend
|
||||
|
||||
1. Go to `/frontend` folder.
|
||||
You can run a webpack dev server that only exposes the frontend files, which can be useful for testing only the UI of the app. There's also a `proxy.conf.json` file which configures the dev server to send the backend requests to port `8000`.
|
||||
|
||||
1. Run `npm install` to install all dependencies.
|
||||
In order to build the UI locally, and expose it with a webpack dev server you will need to:
|
||||
|
||||
It creates `/frontend/node_modules` folder with all dependencies from [`package.json`](https://github.com/kubeflow/katib/blob/master/pkg/ui/v1beta1/frontend/package.json). If you want to add new package, run `npm install <package>@<version>`. That should update `/frontend/package.json` and `/frontend/package-lock.json` with the new dependency.
|
||||
1. Create a module from the [common library](https://github.com/kubeflow/kubeflow/tree/master/components/crud-web-apps/common/frontend/kubeflow-common-lib)
|
||||
2. Install the node modules of the app and also link the common-library module
|
||||
|
||||
### Start frontend server
|
||||
You can build the common library with:
|
||||
|
||||
If you want to edit only frontend without connection to the backend, you can start frontend server in your local environment. For it, run `npm run start` under `/frontend` folder. You can access the UI using this URL: `http://localhost:3000/`.
|
||||
```bash
|
||||
COMMIT=$(cat ./frontend/COMMIT) \
|
||||
&& cd /tmp && git clone https://github.com/kubeflow/kubeflow.git \
|
||||
&& cd kubeflow \
|
||||
&& git checkout $COMMIT \
|
||||
&& cd components/crud-web-apps/common/frontend/kubeflow-common-lib
|
||||
|
||||
### Serve UI frontend and backend
|
||||
# build the common library module
|
||||
npm i
|
||||
npm run build
|
||||
|
||||
You can serve Katib UI locally. To make it you need to follow these steps:
|
||||
# link the module to your npm packages
|
||||
# depending on where you npm stores the global packages you
|
||||
# might need to use sudo
|
||||
npm link dist/kubeflow
|
||||
```
|
||||
|
||||
1. Run `npm run build` under `/frontend` folder. It creates `/frontend/build` directory with optimized production build.
|
||||
And then build and run the UI locally, on `localhost:4200`, with:
|
||||
|
||||
If your `node` memory limit is not enough to build the frontend, you may see this error while building: `FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory`. To fix it, you can try to increase `node` memory limit. For that, change [`build`](https://github.com/kubeflow/katib/blob/master/pkg/ui/v1beta1/frontend/package.json#L28) script to `react-scripts --max_old_space_size=4096 build` to increase `node` memory up to 4 Gb.
|
||||
```bash
|
||||
# If you've already cloned the repo then skip this step and just
|
||||
# navigate to the pkg/ui/v1beta1/frontend dir
|
||||
cd /tmp && git clone https://github.com/kubeflow/katib.git \
|
||||
&& cd katib/pkg/ui/v1beta1/frontend
|
||||
|
||||
npm i
|
||||
npm link kubeflow
|
||||
npm run start
|
||||
```
|
||||
|
||||
### Serve the UI from the backend
|
||||
|
||||
This is the recommended way to test the web app e2e. In order to build the UI and serve it via the backend, locally, you will need to:
|
||||
|
||||
1. Build the UI locally. You have to follow the steps from the previous section, but instead of running `npm run start` you need to run `npm run build:prod`. It builds the frontend artifacts under `frontend/dist/static` folder.
|
||||
|
||||
Moreover, you are able to run `npm run build:watch` instead of `npm run build:prod`. In that case, it starts a process which is watching the source code changes and building the frontend artifacts under `frontend/dist/static` folder.
|
||||
|
||||
Learn more about Angular scripts in the [official guide](https://angular.io/cli/build).
|
||||
|
||||
1. Run `kubectl port-forward svc/katib-db-manager 6789 -n kubeflow` to expose `katib-db-manager` service for external access. You can use [different ways](https://kubernetes.io/docs/tasks/access-application-cluster/) to get external address for Kubernetes service. After exposing service, you should be able to receive information by running `wget <external-ip>:<service-port>`. In case of port-forwarding above, you have to run `wget localhost:6789`.
|
||||
|
||||
1. Go to `cmd/ui/v1beta1`.
|
||||
1. Go to [`cmd/ui/v1beta1`](../../../cmd/ui/v1beta1/)
|
||||
|
||||
1. Run `main.go` file with appropriate flags, where:
|
||||
|
||||
- `--build-dir` - builded frontend directory.
|
||||
- `--build-dir` - directory with the frontend artifacts.
|
||||
- `--port` - port to access Katib UI.
|
||||
- `--db-manager-address` - Katib DB manager external IP and port address.
|
||||
|
||||
For example, if you clone Katib repository to `/home` folder and use port-forwarding to expose `katib-db-manager`, run this command:
|
||||
For example, if you use port-forwarding to expose `katib-db-manager`, run this command:
|
||||
|
||||
```
|
||||
go run main.go --build-dir=/home/katib/pkg/ui/v1beta1/frontend/build --port=8080 --db-manager-address=localhost:6789
|
||||
export APP_DISABLE_AUTH=true
|
||||
go run main.go --build-dir=../../../pkg/ui/v1beta1/frontend/dist --port=8080 --db-manager-address=localhost:6789
|
||||
```
|
||||
|
||||
After that, you can access the UI using this URL: `http://localhost:8080/katib/`.
|
||||
|
||||
## Production
|
||||
|
||||
To run Katib UI in Production, after all changes in frontend and backend, you need to create an image for the UI. Under `/katib` directory run this: `docker build . -f cmd/ui/v1beta1/Dockerfile -t <name of your image>` to build the image. If Docker resources are not enough to build the frontend, you get `node` out of memory error. You can try to increase Docker resources or modify `package.json` as detailed [above](https://github.com/kubeflow/katib/tree/master/pkg/ui/v1beta1#serve-ui-frontend-and-backend) at step 1.
|
||||
To run Katib UI in Production, after all changes in frontend and backend, you need to create an image for the UI. Under `/katib` directory run this: `docker build . -f cmd/ui/v1beta1/Dockerfile -t <name of your image>` to build the image.
|
||||
|
||||
After that, you can modify UI [deployment](https://github.com/kubeflow/katib/blob/master/manifests/v1beta1/components/ui/ui.yaml#L21) with your new image. Then, follow [these steps](https://www.kubeflow.org/docs/components/katib/hyperparameter/#accessing-the-katib-ui) to access Katib UI.
|
||||
After that, you can modify the [UI Deployment](../../../manifests/v1beta1/components/ui/ui.yaml) with
|
||||
your new image. Then, follow
|
||||
[these steps](https://www.kubeflow.org/docs/components/katib/hyperparameter/#accessing-the-katib-ui) to access Katib UI.
|
||||
|
||||
## Code style
|
||||
|
||||
To make frontend code consistent and easy to review we use [Prettier](https://prettier.io/). You can find Prettier config [here](https://github.com/kubeflow/katib/tree/master/pkg/ui/v1beta1/frontend/.prettierrc.yaml).
|
||||
To make frontend code consistent and easy to review we use [Prettier](https://prettier.io/).
|
||||
You can find Prettier config [here](../../../pkg/ui/v1beta1/frontend/.prettierrc.yaml).
|
||||
Check [here](https://prettier.io/docs/en/install.html), how to install Prettier CLI to check and format your code.
|
||||
|
||||
### IDE integration
|
||||
|
||||
+434
-76
@@ -17,19 +17,33 @@ limitations under the License.
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
experimentv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/experiments/v1beta1"
|
||||
suggestionv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/suggestions/v1beta1"
|
||||
trialsv1beta1 "github.com/kubeflow/katib/pkg/apis/controller/trials/v1beta1"
|
||||
api_pb_v1beta1 "github.com/kubeflow/katib/pkg/apis/manager/v1beta1"
|
||||
consts "github.com/kubeflow/katib/pkg/controller.v1beta1/consts"
|
||||
"github.com/kubeflow/katib/pkg/util/v1beta1/katibclient"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
common "github.com/kubeflow/katib/pkg/apis/controller/common/v1beta1"
|
||||
mccommon "github.com/kubeflow/katib/pkg/metricscollector/v1beta1/common"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
)
|
||||
|
||||
func NewKatibUIHandler(dbManagerAddr string) *KatibUIHandler {
|
||||
@@ -38,12 +52,27 @@ func NewKatibUIHandler(dbManagerAddr string) *KatibUIHandler {
|
||||
log.Printf("NewClient for Katib failed: %v", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &KatibUIHandler{
|
||||
katibClient: kclient,
|
||||
dbManagerAddr: dbManagerAddr,
|
||||
}
|
||||
}
|
||||
|
||||
// ServeIndex will return index.html for any non-API URL
|
||||
func (k *KatibUIHandler) ServeIndex(buildDir string) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
fp := filepath.Join(buildDir, "static/index.html")
|
||||
log.Printf("Sending file %s for url: %s", fp, r.URL)
|
||||
|
||||
// never cache index.html
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0")
|
||||
|
||||
// return the contents of index.html
|
||||
http.ServeFile(w, r, fp)
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) connectManager() (*grpc.ClientConn, api_pb_v1beta1.DBManagerClient) {
|
||||
conn, err := grpc.Dial(k.dbManagerAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
@@ -54,7 +83,7 @@ func (k *KatibUIHandler) connectManager() (*grpc.ClientConn, api_pb_v1beta1.DBMa
|
||||
return conn, c
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) SubmitYamlJob(w http.ResponseWriter, r *http.Request) {
|
||||
func (k *KatibUIHandler) CreateExperiment(w http.ResponseWriter, r *http.Request) {
|
||||
var data map[string]interface{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
@@ -63,72 +92,73 @@ func (k *KatibUIHandler) SubmitYamlJob(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
job := experimentv1beta1.Experiment{}
|
||||
if yamlContent, ok := data["yaml"].(string); ok {
|
||||
err := yaml.Unmarshal([]byte(yamlContent), &job)
|
||||
if err != nil {
|
||||
log.Printf("Unmarshal YAML content failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
err = k.katibClient.CreateRuntimeObject(&job)
|
||||
if err != nil {
|
||||
log.Printf("CreateRuntimeObject from YAML failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) SubmitParamsJob(w http.ResponseWriter, r *http.Request) {
|
||||
//enableCors(&w)
|
||||
var data map[string]interface{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
log.Printf("Failed to decode body: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
dataJSON, ok := data["postData"]
|
||||
if !ok {
|
||||
msg := "Couldn't load the 'postData' field of the request's data"
|
||||
log.Println(msg)
|
||||
http.Error(w, msg, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if data, ok := data["postData"]; ok {
|
||||
jsonbody, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
log.Printf("Marshal data for experiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
job := experimentv1beta1.Experiment{}
|
||||
if err := json.Unmarshal(jsonbody, &job); err != nil {
|
||||
log.Printf("Unmarshal experiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
dataMap := data.(map[string]interface{})
|
||||
job.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: "kubeflow.org/v1beta1",
|
||||
Kind: "Experiment",
|
||||
}
|
||||
job.ObjectMeta = metav1.ObjectMeta{
|
||||
Name: dataMap["metadata"].(map[string]interface{})["name"].(string),
|
||||
Namespace: dataMap["metadata"].(map[string]interface{})["namespace"].(string),
|
||||
}
|
||||
err = k.katibClient.CreateRuntimeObject(&job)
|
||||
if err != nil {
|
||||
log.Printf("CreateRuntimeObject from parameters failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FetchAllExperiments gets HP and NAS experiments in all namespaces.
|
||||
func (k *KatibUIHandler) FetchAllExperiments(w http.ResponseWriter, r *http.Request) {
|
||||
// At first, try to list experiments in cluster scope
|
||||
experiments, err := k.getExperiments([]string{""})
|
||||
jsonbody, err := json.Marshal(dataJSON)
|
||||
if err != nil {
|
||||
// If failed, just try to list experiments from own namespace
|
||||
experiments, err = k.getExperiments([]string{})
|
||||
log.Printf("Marshal data for experiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
job := experimentv1beta1.Experiment{}
|
||||
if err := json.Unmarshal(jsonbody, &job); err != nil {
|
||||
log.Printf("Unmarshal experiment failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
namespace := job.ObjectMeta.Namespace
|
||||
experimentName := job.ObjectMeta.Name
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeCreate, namespace, consts.PluralExperiment, "", experimentName, experimentv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to create experiment: %s in namespace: %s \n", user, experimentName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
err = k.katibClient.CreateRuntimeObject(&job)
|
||||
if err != nil {
|
||||
log.Printf("CreateRuntimeObject from parameters failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) FetchExperiments(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No 'namespace' query parameter was provided.")
|
||||
err := errors.New("no 'namespace' query parameter was provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeList, namespace, consts.PluralExperiment, "", "", experimentv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to list experiments in namespace: %s \n", user, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
experiments, err := k.getExperiments([]string{namespace})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -139,16 +169,44 @@ func (k *KatibUIHandler) FetchAllExperiments(w http.ResponseWriter, r *http.Requ
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write experiments failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (k *KatibUIHandler) DeleteExperiment(w http.ResponseWriter, r *http.Request) {
|
||||
experimentName := r.URL.Query()["experimentName"][0]
|
||||
namespace := r.URL.Query()["namespace"][0]
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No 'namespace' query parameter was provided.")
|
||||
err := errors.New("no 'namespace' query parameter was provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
experimentNames, ok := r.URL.Query()["experimentName"]
|
||||
if !ok {
|
||||
log.Printf("No experimentName provided in Query parameteres! Provide an 'experimentName' param")
|
||||
err := errors.New("no experimentName provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
experimentName := experimentNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeDelete, namespace, consts.PluralExperiment, "", experimentName, experimentv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to delete experiment: %s in namespace: %s \n", user, experimentName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
experiment, err := k.katibClient.GetExperiment(experimentName, namespace)
|
||||
if err != nil {
|
||||
@@ -156,6 +214,7 @@ func (k *KatibUIHandler) DeleteExperiment(w http.ResponseWriter, r *http.Request
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = k.katibClient.DeleteRuntimeObject(experiment)
|
||||
if err != nil {
|
||||
log.Printf("DeleteRuntimeObject failed: %v", err)
|
||||
@@ -196,14 +255,16 @@ func (k *KatibUIHandler) DeleteExperiment(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
panic(err)
|
||||
log.Printf("Write HP and NAS experiments failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FetchTrialTemplates gets all trial templates in all namespaces
|
||||
func (k *KatibUIHandler) FetchTrialTemplates(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
trialTemplatesViewList, err := k.getTrialTemplatesViewList()
|
||||
trialTemplatesViewList, err := k.getTrialTemplatesViewList(r)
|
||||
if err != nil {
|
||||
log.Printf("getTrialTemplatesViewList failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@@ -220,7 +281,7 @@ func (k *KatibUIHandler) FetchTrialTemplates(w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write templates failed: %v", err)
|
||||
log.Printf("Write template failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -228,6 +289,7 @@ func (k *KatibUIHandler) FetchTrialTemplates(w http.ResponseWriter, r *http.Requ
|
||||
|
||||
// AddTemplate adds template to ConfigMap
|
||||
func (k *KatibUIHandler) AddTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var data map[string]interface{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
log.Printf("Failed to decode body: %v", err)
|
||||
@@ -240,7 +302,18 @@ func (k *KatibUIHandler) AddTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
updatedConfigMapPath := data["updatedConfigMapPath"].(string)
|
||||
updatedTemplateYaml := data["updatedTemplateYaml"].(string)
|
||||
|
||||
newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, "", updatedConfigMapPath, updatedTemplateYaml, ActionTypeAdd)
|
||||
user, err := IsAuthorized(consts.ActionTypeCreate, updatedConfigMapNamespace, corev1.ResourceConfigMaps.String(), "", updatedConfigMapName, corev1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to add configmap: %s in namespace: %s \n", user, updatedConfigMapName, updatedConfigMapNamespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, "", updatedConfigMapPath, updatedTemplateYaml, ActionTypeAdd, r)
|
||||
if err != nil {
|
||||
log.Printf("updateTrialTemplates failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@@ -280,7 +353,18 @@ func (k *KatibUIHandler) EditTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
updatedConfigMapPath := data["updatedConfigMapPath"].(string)
|
||||
updatedTemplateYaml := data["updatedTemplateYaml"].(string)
|
||||
|
||||
newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, configMapPath, updatedConfigMapPath, updatedTemplateYaml, ActionTypeEdit)
|
||||
user, err := IsAuthorized(consts.ActionTypeUpdate, updatedConfigMapNamespace, corev1.ResourceConfigMaps.String(), "", updatedConfigMapName, corev1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to edit configmap: %s in namespace: %s \n", user, updatedConfigMapName, updatedConfigMapNamespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, configMapPath, updatedConfigMapPath, updatedTemplateYaml, ActionTypeEdit, r)
|
||||
if err != nil {
|
||||
log.Printf("updateTrialTemplates failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@@ -317,7 +401,18 @@ func (k *KatibUIHandler) DeleteTemplate(w http.ResponseWriter, r *http.Request)
|
||||
updatedConfigMapName := data["updatedConfigMapName"].(string)
|
||||
updatedConfigMapPath := data["updatedConfigMapPath"].(string)
|
||||
|
||||
newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, "", updatedConfigMapPath, "", ActionTypeDelete)
|
||||
user, err := IsAuthorized(consts.ActionTypeDelete, updatedConfigMapNamespace, corev1.ResourceConfigMaps.String(), "", updatedConfigMapName, corev1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to delete configmap: %s in namespace: %s \n", user, updatedConfigMapName, updatedConfigMapNamespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
newTemplates, err := k.updateTrialTemplates(updatedConfigMapNamespace, updatedConfigMapName, "", updatedConfigMapPath, "", ActionTypeDelete, r)
|
||||
if err != nil {
|
||||
log.Printf("updateTrialTemplates failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@@ -366,8 +461,35 @@ func (k *KatibUIHandler) FetchNamespaces(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
// FetchExperiment gets experiment in specific namespace.
|
||||
func (k *KatibUIHandler) FetchExperiment(w http.ResponseWriter, r *http.Request) {
|
||||
experimentName := r.URL.Query()["experimentName"][0]
|
||||
namespace := r.URL.Query()["namespace"][0]
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No 'namespace' query parameter was provided.")
|
||||
err := errors.New("no 'namespace' query parameter was provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
experimentNames, ok := r.URL.Query()["experimentName"]
|
||||
if !ok {
|
||||
log.Printf("No experimentName provided in Query parameteres! Provide an 'experimentName' param")
|
||||
err := errors.New("no experimentName provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
experimentName := experimentNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, namespace, consts.PluralExperiment, "", experimentName, experimentv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get experiment: %s in namespace: %s \n", user, experimentName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
experiment, err := k.katibClient.GetExperiment(experimentName, namespace)
|
||||
if err != nil {
|
||||
@@ -390,8 +512,35 @@ func (k *KatibUIHandler) FetchExperiment(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
// FetchSuggestion gets suggestion in specific namespace
|
||||
func (k *KatibUIHandler) FetchSuggestion(w http.ResponseWriter, r *http.Request) {
|
||||
suggestionName := r.URL.Query()["suggestionName"][0]
|
||||
namespace := r.URL.Query()["namespace"][0]
|
||||
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No namespace provided in Query parameters! Provide a 'namespace' param")
|
||||
err := errors.New("no 'namespace' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
suggestionNames, ok := r.URL.Query()["suggestionName"]
|
||||
if !ok {
|
||||
log.Printf("No experimentName provided in Query parameteres! Provide an 'experimentName' param")
|
||||
err := errors.New("no experimentName provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
suggestionName := suggestionNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, namespace, consts.PluralSuggestion, "", suggestionName, suggestionv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get suggestion: %s in namespace: %s \n", user, suggestionName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
suggestion, err := k.katibClient.GetSuggestion(suggestionName, namespace)
|
||||
if err != nil {
|
||||
@@ -412,3 +561,212 @@ func (k *KatibUIHandler) FetchSuggestion(w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FetchTrial gets trial in specific namespace.
|
||||
func (k *KatibUIHandler) FetchTrial(w http.ResponseWriter, r *http.Request) {
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No namespace provided in Query parameters! Provide a 'namespace' param")
|
||||
err := errors.New("no 'namespace' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialNames, ok := r.URL.Query()["trialName"]
|
||||
if !ok {
|
||||
log.Printf("No trialName provided in Query parameters! Provide a 'trialName' param")
|
||||
err := errors.New("no 'trialName' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialName := trialNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, namespace, consts.PluralTrial, "", trialName, trialsv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get trial: %s from namespace: %s \n", user, trialName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
trial, err := k.katibClient.GetTrial(trialName, namespace)
|
||||
if err != nil {
|
||||
log.Printf("GetTrial failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response, err := json.Marshal(trial)
|
||||
if err != nil {
|
||||
log.Printf("Marshal Trial failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write trial failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// FetchTrialLogs fetches logs for a trial in specific namespace.
|
||||
func (k *KatibUIHandler) FetchTrialLogs(w http.ResponseWriter, r *http.Request) {
|
||||
namespaces, ok := r.URL.Query()["namespace"]
|
||||
if !ok {
|
||||
log.Printf("No namespace provided in Query parameters! Provide a 'namespace' param")
|
||||
err := errors.New("no 'namespace' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialNames, ok := r.URL.Query()["trialName"]
|
||||
if !ok {
|
||||
log.Printf("No trialName provided in Query parameters! Provide a 'trialName' param")
|
||||
err := errors.New("no 'trialName' provided")
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
trialName := trialNames[0]
|
||||
namespace := namespaces[0]
|
||||
|
||||
user, err := IsAuthorized(consts.ActionTypeGet, namespace, consts.PluralTrial, "", trialName, trialsv1beta1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get trial: %s in namespace: %s \n", user, trialName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
trial := &trialsv1beta1.Trial{}
|
||||
if err := k.katibClient.GetClient().Get(context.Background(), types.NamespacedName{Name: trialName, Namespace: namespace}, trial); err != nil {
|
||||
log.Printf("GetLogs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Use controller-runtime client instead of kubernetes client to get logs, once this is available
|
||||
clientset, err := createKubernetesClientset()
|
||||
if err != nil {
|
||||
log.Printf("GetLogs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
podName, err := fetchMasterPodName(clientset, trial)
|
||||
if err != nil {
|
||||
log.Printf("GetLogs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
user, err = IsAuthorized(consts.ActionTypeGet, namespace, corev1.ResourcePods.String(), "log", podName, corev1.SchemeGroupVersion, k.katibClient.GetClient(), r)
|
||||
if user == "" && err != nil {
|
||||
log.Printf("No user provided in kubeflow-userid header.")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
} else if err != nil {
|
||||
log.Printf("The user: %s is not authorized to get pod logs: %s in namespace: %s \n", user, podName, namespace)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
podLogOpts := corev1.PodLogOptions{}
|
||||
podLogOpts.Container = trial.Spec.PrimaryContainerName
|
||||
if trial.Spec.MetricsCollector.Collector.Kind == common.StdOutCollector {
|
||||
podLogOpts.Container = mccommon.MetricLoggerCollectorContainerName
|
||||
}
|
||||
|
||||
logs, err := fetchPodLogs(clientset, namespace, podName, podLogOpts)
|
||||
if err != nil {
|
||||
log.Printf("GetLogs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
response, err := json.Marshal(logs)
|
||||
if err != nil {
|
||||
log.Printf("Marshal logs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if _, err = w.Write(response); err != nil {
|
||||
log.Printf("Write logs failed: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// createKubernetesClientset returns kubernetes clientset
|
||||
func createKubernetesClientset() (*kubernetes.Clientset, error) {
|
||||
cfg, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clientset, nil
|
||||
}
|
||||
|
||||
// fetchMasterPodName returns name of the master pod for a trial
|
||||
func fetchMasterPodName(clientset *kubernetes.Clientset, trial *trialsv1beta1.Trial) (string, error) {
|
||||
selectionLabel := consts.LabelTrialName + "=" + trial.ObjectMeta.Name
|
||||
for primaryKey, primaryValue := range trial.Spec.PrimaryPodLabels {
|
||||
selectionLabel = selectionLabel + "," + primaryKey + "=" + primaryValue
|
||||
}
|
||||
|
||||
podList, err := clientset.CoreV1().Pods(trial.ObjectMeta.Namespace).List(context.Background(), metav1.ListOptions{LabelSelector: selectionLabel})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(podList.Items) == 0 {
|
||||
return "", errors.New(`Failed to find logs for this Trial. Make sure you've set "spec.trialTemplate.retain"
|
||||
field to "true" in the Experiment definition. If this error persists then the Pod's logs are not currently
|
||||
persisted in the cluster.`)
|
||||
}
|
||||
|
||||
// If Pod is Running or Succeeded Pod, return it.
|
||||
for _, pod := range podList.Items {
|
||||
if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodRunning {
|
||||
return pod.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, return the first Failed Pod.
|
||||
for _, pod := range podList.Items {
|
||||
if pod.Status.Phase == corev1.PodFailed {
|
||||
return pod.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, return error since Pod is in the Pending state.
|
||||
return "", errors.New("Failed to get logs for this Trial. Pod is in the Pending or Unknown state.")
|
||||
}
|
||||
|
||||
// fetchPodLogs returns logs of a pod for the given job name and namespace
|
||||
func fetchPodLogs(clientset *kubernetes.Clientset, namespace string, podName string, podLogOpts corev1.PodLogOptions) (string, error) {
|
||||
req := clientset.CoreV1().Pods(namespace).GetLogs(podName, &podLogOpts)
|
||||
podLogs, err := req.Stream(context.Background())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer podLogs.Close()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = io.Copy(buf, podLogs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
str := buf.String()
|
||||
|
||||
return str, nil
|
||||
}
|
||||
|
||||
Executable → Regular
+47
-14
@@ -1,23 +1,56 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
Thumbs.db
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
# Rok files
|
||||
**/browse-in-rok*
|
||||
|
||||
# build
|
||||
/build
|
||||
# Font files (automatically generating)
|
||||
/src/assets/fonts
|
||||
|
||||
# Cypress
|
||||
cypress/screenshots
|
||||
cypress/downloads
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
singleQuote: true
|
||||
printWidth: 100
|
||||
printWidth: 80
|
||||
trailingComma: all
|
||||
tabWidth: 2
|
||||
arrowParens: avoid
|
||||
|
||||
+12329
-13856
File diff suppressed because it is too large
Load Diff
@@ -1,37 +1,84 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"homepage": ".",
|
||||
"version": "0.1.0",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "npm run copyLibAssets && ng serve --proxy-config proxy.conf.json",
|
||||
"build:watch": "npm run copyLibAssets && ng build --base-href /katib/ --deploy-url /katib/static/ --outputPath dist/static --watch",
|
||||
"build:prod": "npm run copyLibAssets && ng build --base-href /katib/ --deploy-url /katib/static/ --outputPath dist/static --configuration production",
|
||||
"copyLibAssets": "cp -r ./node_modules/kubeflow/assets/* ./src/assets/",
|
||||
"test": "ng test",
|
||||
"test:prod": "ng test --browsers=ChromeHeadless --watch=false",
|
||||
"lint-check": "ng lint",
|
||||
"lint": "ng lint --fix",
|
||||
"format:check": "prettier --check 'src/**/*.{jsx,js,ts,html,scss,css}' || node scripts/check-format-error.js",
|
||||
"format:write": "prettier --write 'src/**/*.{jsx,js,ts,html,scss,css}'",
|
||||
"ui-test": "cypress open . --e2e",
|
||||
"ui-test-ci": "cypress run . --e2e",
|
||||
"ui-test-ci-all": "cypress run . --e2e --browser=chrome && cypress run . --e2e --browser=firefox"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"ace-builds": "^1.4.8",
|
||||
"axios": "^0.21.2",
|
||||
"graphviz-react": "^1.0.6",
|
||||
"js-yaml": "^3.13.1",
|
||||
"plotly.js": "^1.45.0",
|
||||
"react": "^16.8.3",
|
||||
"react-ace": "^8.0.0",
|
||||
"react-dom": "^16.8.3",
|
||||
"react-plotly.js": "^2.3.0",
|
||||
"react-redux": "^6.0.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"redux": "^4.0.1",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-saga": "^1.0.1"
|
||||
"@angular/animations": "~12.2.7",
|
||||
"@angular/cdk": "^12.2.7",
|
||||
"@angular/cdk-experimental": "^12.2.7",
|
||||
"@angular/common": "~12.2.7",
|
||||
"@angular/compiler": "~12.2.7",
|
||||
"@angular/core": "~12.2.7",
|
||||
"@angular/forms": "~12.2.7",
|
||||
"@angular/localize": "^12.2.7",
|
||||
"@angular/material": "^12.2.7",
|
||||
"@angular/platform-browser": "~12.2.7",
|
||||
"@angular/platform-browser-dynamic": "~12.2.7",
|
||||
"@angular/router": "~12.2.7",
|
||||
"@fortawesome/angular-fontawesome": "^0.9.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.26",
|
||||
"@fortawesome/free-brands-svg-icons": "^5.12.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.12.0",
|
||||
"@swimlane/ngx-charts": "^19.0.1",
|
||||
"date-fns": "1.29.0",
|
||||
"echarts": "^5.3.2",
|
||||
"echarts-stat": "^1.2.0",
|
||||
"js-yaml": "^4.0.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"material-icons": "^0.7.7",
|
||||
"monaco-editor": "^0.33.0",
|
||||
"ngx-echarts": "^8.0.1",
|
||||
"ngx-json-viewer": "^2.4.0",
|
||||
"rxjs": "~6.6.7",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"format:check": "prettier --check 'src/**/*.{jsx,js}' || node scripts/check-format-error.js",
|
||||
"format:write": "prettier --write 'src/**/*.{jsx,js}'"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not ie <= 11",
|
||||
"not op_mini all"
|
||||
]
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~12.2.7",
|
||||
"@angular-eslint/builder": "12.7.0",
|
||||
"@angular-eslint/eslint-plugin": "12.7.0",
|
||||
"@angular-eslint/eslint-plugin-template": "12.7.0",
|
||||
"@angular-eslint/schematics": "12.7.0",
|
||||
"@angular-eslint/template-parser": "12.7.0",
|
||||
"@angular/cli": "~12.2.7",
|
||||
"@angular/compiler-cli": "~12.2.7",
|
||||
"@angular/language-service": "~12.2.7",
|
||||
"@cypress/schematic": "^2.3.0",
|
||||
"@kubernetes/client-node": "^0.12.2",
|
||||
"@types/jasmine": "~3.6.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"@typescript-eslint/eslint-plugin": "4.28.2",
|
||||
"@typescript-eslint/parser": "4.28.2",
|
||||
"codelyzer": "^6.0.0",
|
||||
"cypress": "^10.10.0",
|
||||
"eslint": "^7.26.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~6.3.16",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"prettier": "2.3.2",
|
||||
"ts-node": "~7.0.0",
|
||||
"typescript": "~4.3.5",
|
||||
"wait-on": "^7.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script defer src="/dashboard_lib.bundle.js"></script>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/logo.png" />
|
||||
<meta http-equiv="Access-Control-Allow-Origin" content="*" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<style>
|
||||
body {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
</style>
|
||||
<title>Katib UI</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 79 KiB |
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"short_name": "Katib UI",
|
||||
"name": "Dashboard for Katib",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
export const TOGGLE_MENU = 'TOGGLE_MENU';
|
||||
|
||||
export const toggleMenu = state => {
|
||||
return {
|
||||
type: TOGGLE_MENU,
|
||||
state,
|
||||
};
|
||||
};
|
||||
|
||||
export const CLOSE_SNACKBAR = 'CLOSE_SNACKBAR';
|
||||
|
||||
export const closeSnackbar = () => {
|
||||
return {
|
||||
type: CLOSE_SNACKBAR,
|
||||
};
|
||||
};
|
||||
|
||||
export const SUBMIT_YAML_REQUEST = 'SUBMIT_YAML_REQUEST';
|
||||
export const SUBMIT_YAML_FAILURE = 'SUBMIT_YAML_FAILURE';
|
||||
export const SUBMIT_YAML_SUCCESS = 'SUBMIT_YAML_SUCCESS';
|
||||
|
||||
export const submitYaml = (yaml, globalNamespace) => ({
|
||||
type: SUBMIT_YAML_REQUEST,
|
||||
yaml,
|
||||
globalNamespace,
|
||||
});
|
||||
|
||||
export const DELETE_EXPERIMENT_REQUEST = 'DELETE_EXPERIMENT_REQUEST';
|
||||
export const DELETE_EXPERIMENT_FAILURE = 'DELETE_EXPERIMENT_FAILURE';
|
||||
export const DELETE_EXPERIMENT_SUCCESS = 'DELETE_EXPERIMENT_SUCCESS';
|
||||
|
||||
export const deleteExperiment = (name, namespace) => ({
|
||||
type: DELETE_EXPERIMENT_REQUEST,
|
||||
name,
|
||||
namespace,
|
||||
});
|
||||
|
||||
export const OPEN_DELETE_EXPERIMENT_DIALOG = 'OPEN_DELETE_EXPERIMENT_DIALOG';
|
||||
|
||||
export const openDeleteExperimentDialog = (name, namespace) => ({
|
||||
type: OPEN_DELETE_EXPERIMENT_DIALOG,
|
||||
name,
|
||||
namespace,
|
||||
});
|
||||
|
||||
export const CLOSE_DELETE_EXPERIMENT_DIALOG = 'CLOSE_DELETE_EXPERIMENT_DIALOG';
|
||||
|
||||
export const closeDeleteExperimentDialog = () => ({
|
||||
type: CLOSE_DELETE_EXPERIMENT_DIALOG,
|
||||
});
|
||||
|
||||
export const FETCH_NAMESPACES_REQUEST = 'FETCH_NAMESPACES_REQUEST';
|
||||
export const FETCH_NAMESPACES_SUCCESS = 'FETCH_NAMESPACES_SUCCESS';
|
||||
export const FETCH_NAMESPACES_FAILURE = 'FETCH_NAMESPACES_FAILURE';
|
||||
|
||||
export const fetchNamespaces = () => ({
|
||||
type: FETCH_NAMESPACES_REQUEST,
|
||||
});
|
||||
|
||||
export const CHANGE_GLOBAL_NAMESPACE = 'CHANGE_GLOBAL_NAMESPACE';
|
||||
|
||||
export const FETCH_EXPERIMENT_REQUEST = 'FETCH_EXPERIMENT_REQUEST';
|
||||
export const FETCH_EXPERIMENT_SUCCESS = 'FETCH_EXPERIMENT_SUCCESS';
|
||||
export const FETCH_EXPERIMENT_FAILURE = 'FETCH_EXPERIMENT_FAILURE';
|
||||
|
||||
export const fetchExperiment = (name, namespace) => ({
|
||||
type: FETCH_EXPERIMENT_REQUEST,
|
||||
name,
|
||||
namespace,
|
||||
});
|
||||
|
||||
export const CLOSE_DIALOG_EXPERIMENT = 'CLOSE_DIALOG_EXPERIMENT';
|
||||
|
||||
export const closeDialogExperiment = () => ({
|
||||
type: CLOSE_DIALOG_EXPERIMENT,
|
||||
});
|
||||
|
||||
export const FETCH_SUGGESTION_REQUEST = 'FETCH_SUGGESTION_REQUEST';
|
||||
export const FETCH_SUGGESTION_SUCCESS = 'FETCH_SUGGESTION_SUCCESS';
|
||||
export const FETCH_SUGGESTION_FAILURE = 'FETCH_SUGGESTION_FAILURE';
|
||||
|
||||
export const fetchSuggestion = (name, namespace) => ({
|
||||
type: FETCH_SUGGESTION_REQUEST,
|
||||
name,
|
||||
namespace,
|
||||
});
|
||||
|
||||
export const CLOSE_DIALOG_SUGGESTION = 'CLOSE_DIALOG_SUGGESTION';
|
||||
|
||||
export const closeDialogSuggestion = () => ({
|
||||
type: CLOSE_DIALOG_SUGGESTION,
|
||||
});
|
||||
|
||||
export const FETCH_EXPERIMENTS_REQUEST = 'FETCH_EXPERIMENTS_REQUEST';
|
||||
export const FETCH_EXPERIMENTS_SUCCESS = 'FETCH_EXPERIMENTS_SUCCESS';
|
||||
export const FETCH_EXPERIMENTS_FAILURE = 'FETCH_EXPERIMENTS_FAILURE';
|
||||
|
||||
export const fetchExperiments = () => ({
|
||||
type: FETCH_EXPERIMENTS_REQUEST,
|
||||
});
|
||||
|
||||
export const FILTER_EXPERIMENTS = 'FILTER_EXPERIMENTS';
|
||||
|
||||
export const filterExperiments = (experimentName, experimentNamespace) => ({
|
||||
type: FILTER_EXPERIMENTS,
|
||||
experimentName,
|
||||
experimentNamespace,
|
||||
});
|
||||
|
||||
export const CHANGE_STATUS = 'CHANGE_STATUS';
|
||||
|
||||
export const changeStatus = (filter, checked) => ({
|
||||
type: CHANGE_STATUS,
|
||||
filter,
|
||||
checked,
|
||||
});
|
||||
|
||||
export const CHANGE_EARLY_STOPPING_ALGORITHM = 'CHANGE_EARLY_STOPPING_ALGORITHM';
|
||||
|
||||
export const changeEarlyStoppingAlgorithm = algorithmName => ({
|
||||
type: CHANGE_EARLY_STOPPING_ALGORITHM,
|
||||
algorithmName,
|
||||
});
|
||||
|
||||
export const ADD_EARLY_STOPPING_SETTING = 'ADD_EARLY_STOPPING_SETTING';
|
||||
|
||||
export const addEarlyStoppingSetting = () => ({
|
||||
type: ADD_EARLY_STOPPING_SETTING,
|
||||
});
|
||||
|
||||
export const CHANGE_EARLY_STOPPING_SETTING = 'CHANGE_EARLY_STOPPING_SETTING';
|
||||
|
||||
export const changeEarlyStoppingSetting = (index, field, value) => ({
|
||||
type: CHANGE_EARLY_STOPPING_SETTING,
|
||||
index,
|
||||
field,
|
||||
value,
|
||||
});
|
||||
|
||||
export const DELETE_EARLY_STOPPING_SETTING = 'DELETE_EARLY_STOPPING_SETTING';
|
||||
|
||||
export const deleteEarlyStoppingSetting = index => ({
|
||||
type: DELETE_EARLY_STOPPING_SETTING,
|
||||
index,
|
||||
});
|
||||
|
||||
export const CHANGE_TRIAL_TEMPLATE_SOURCE = 'CHANGE_TRIAL_TEMPLATE_SOURCE';
|
||||
|
||||
export const changeTrialTemplateSource = source => ({
|
||||
type: CHANGE_TRIAL_TEMPLATE_SOURCE,
|
||||
source,
|
||||
});
|
||||
|
||||
export const ADD_PRIMARY_POD_LABEL = 'ADD_PRIMARY_POD_LABEL';
|
||||
|
||||
export const addPrimaryPodLabel = () => ({
|
||||
type: ADD_PRIMARY_POD_LABEL,
|
||||
});
|
||||
|
||||
export const CHANGE_PRIMARY_POD_LABEL = 'CHANGE_PRIMARY_POD_LABEL';
|
||||
|
||||
export const changePrimaryPodLabel = (fieldName, index, value) => ({
|
||||
type: CHANGE_PRIMARY_POD_LABEL,
|
||||
fieldName,
|
||||
index,
|
||||
value,
|
||||
});
|
||||
|
||||
export const DELETE_PRIMARY_POD_LABEL = 'DELETE_PRIMARY_POD_LABEL';
|
||||
|
||||
export const deletePrimaryPodLabel = index => ({
|
||||
type: DELETE_PRIMARY_POD_LABEL,
|
||||
index,
|
||||
});
|
||||
|
||||
export const CHANGE_TRIAL_TEMPLATE_SPEC = 'CHANGE_TRIAL_TEMPLATE_SPEC';
|
||||
|
||||
export const changeTrialTemplateSpec = (name, value) => ({
|
||||
type: CHANGE_TRIAL_TEMPLATE_SPEC,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
|
||||
export const FILTER_TEMPLATES_EXPERIMENT = 'FILTER_TEMPLATES_EXPERIMENT';
|
||||
|
||||
export const filterTemplatesExperiment = (
|
||||
configMapNamespaceIndex,
|
||||
configMapNameIndex,
|
||||
configMapPathIndex,
|
||||
) => ({
|
||||
type: FILTER_TEMPLATES_EXPERIMENT,
|
||||
configMapNamespaceIndex,
|
||||
configMapNameIndex,
|
||||
configMapPathIndex,
|
||||
});
|
||||
|
||||
export const CHANGE_TRIAL_TEMPLATE_YAML = 'CHANGE_TRIAL_TEMPLATE_YAML';
|
||||
|
||||
export const changeTrialTemplateYAML = templateYAML => ({
|
||||
type: CHANGE_TRIAL_TEMPLATE_YAML,
|
||||
templateYAML,
|
||||
});
|
||||
|
||||
export const CHANGE_TRIAL_PARAMETERS = 'CHANGE_TRIAL_PARAMETERS';
|
||||
|
||||
export const changeTrialParameters = (index, name, reference, description) => ({
|
||||
type: CHANGE_TRIAL_PARAMETERS,
|
||||
index,
|
||||
name,
|
||||
reference,
|
||||
description,
|
||||
});
|
||||
|
||||
export const VALIDATION_ERROR = 'VALIDATION_ERROR';
|
||||
|
||||
export const validationError = message => ({
|
||||
type: VALIDATION_ERROR,
|
||||
message,
|
||||
});
|
||||
@@ -1,217 +0,0 @@
|
||||
export const CHANGE_YAML_HP = 'CHANGE_YAML_HP';
|
||||
|
||||
export const changeYaml = yaml => ({
|
||||
type: CHANGE_YAML_HP,
|
||||
payload: yaml,
|
||||
});
|
||||
|
||||
export const CHANGE_META_HP = 'CHANGE_META_HP';
|
||||
|
||||
export const changeMeta = (name, value) => ({
|
||||
type: CHANGE_META_HP,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
|
||||
export const CHANGE_SPEC_HP = 'CHANGE_SPEC_HP';
|
||||
|
||||
export const changeSpec = (name, value) => ({
|
||||
type: CHANGE_SPEC_HP,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
|
||||
export const CHANGE_OBJECTIVE_HP = 'CHANGE_OBJECTIVE_HP';
|
||||
|
||||
export const changeObjective = (name, value) => ({
|
||||
type: CHANGE_OBJECTIVE_HP,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
export const ADD_METRICS_HP = 'ADD_METRICS_HP';
|
||||
|
||||
export const addMetrics = () => ({
|
||||
type: ADD_METRICS_HP,
|
||||
});
|
||||
|
||||
export const DELETE_METRICS_HP = 'DELETE_METRICS_HP';
|
||||
|
||||
export const deleteMetrics = index => ({
|
||||
type: DELETE_METRICS_HP,
|
||||
index,
|
||||
});
|
||||
|
||||
export const EDIT_METRICS_HP = 'EDIT_METRICS_HP';
|
||||
|
||||
export const editMetrics = (index, value) => ({
|
||||
type: EDIT_METRICS_HP,
|
||||
index,
|
||||
value,
|
||||
});
|
||||
|
||||
export const CHANGE_METRIC_STRATEGY_HP = 'CHANGE_METRIC_STRATEGY_HP';
|
||||
|
||||
export const metricStrategyChange = (index, strategy) => ({
|
||||
type: CHANGE_METRIC_STRATEGY_HP,
|
||||
index,
|
||||
strategy,
|
||||
});
|
||||
|
||||
export const CHANGE_ALGORITHM_NAME_HP = 'CHANGE_ALGORITHM_NAME_HP';
|
||||
|
||||
export const changeAlgorithmName = algorithmName => ({
|
||||
type: CHANGE_ALGORITHM_NAME_HP,
|
||||
algorithmName,
|
||||
});
|
||||
|
||||
export const ADD_ALGORITHM_SETTING_HP = 'ADD_ALGORITHM_SETTING_HP';
|
||||
|
||||
export const addAlgorithmSetting = () => ({
|
||||
type: ADD_ALGORITHM_SETTING_HP,
|
||||
});
|
||||
|
||||
export const CHANGE_ALGORITHM_SETTING_HP = 'CHANGE_ALGORITHM_SETTING_HP';
|
||||
|
||||
export const changeAlgorithmSetting = (index, field, value) => ({
|
||||
type: CHANGE_ALGORITHM_SETTING_HP,
|
||||
field,
|
||||
value,
|
||||
index,
|
||||
});
|
||||
|
||||
export const DELETE_ALGORITHM_SETTING_HP = 'DELETE_ALGORITHM_SETTING_HP';
|
||||
|
||||
export const deleteAlgorithmSetting = index => ({
|
||||
type: DELETE_ALGORITHM_SETTING_HP,
|
||||
index,
|
||||
});
|
||||
|
||||
export const ADD_PARAMETER_HP = 'CHANGE_PARAMETER_HP';
|
||||
|
||||
export const addParameter = () => ({
|
||||
type: ADD_PARAMETER_HP,
|
||||
});
|
||||
|
||||
export const EDIT_PARAMETER_HP = 'EDIT_PARAMTER_HP';
|
||||
|
||||
export const editParameter = (index, field, value) => ({
|
||||
type: EDIT_PARAMETER_HP,
|
||||
index,
|
||||
field,
|
||||
value,
|
||||
});
|
||||
|
||||
export const DELETE_PARAMETER_HP = 'DELETE_PARAMETER_HP';
|
||||
|
||||
export const deleteParameter = index => ({
|
||||
type: DELETE_PARAMETER_HP,
|
||||
index,
|
||||
});
|
||||
|
||||
export const ADD_LIST_PARAMETER_HP = 'ADD_LIST_PARAMETER_HP';
|
||||
|
||||
export const addListParameter = paramIndex => ({
|
||||
type: ADD_LIST_PARAMETER_HP,
|
||||
paramIndex,
|
||||
});
|
||||
|
||||
export const EDIT_LIST_PARAMETER_HP = 'EDIT_LIST_PARAMETER_HP';
|
||||
|
||||
export const editListParameter = (paramIndex, index, value) => ({
|
||||
type: EDIT_LIST_PARAMETER_HP,
|
||||
paramIndex,
|
||||
index,
|
||||
value,
|
||||
});
|
||||
|
||||
export const DELETE_LIST_PARAMETER_HP = 'DELETE_LIST_PARAMETER_HP';
|
||||
|
||||
export const deleteListParameter = (paramIndex, index) => ({
|
||||
type: DELETE_LIST_PARAMETER_HP,
|
||||
paramIndex,
|
||||
index,
|
||||
});
|
||||
|
||||
export const SUBMIT_HP_JOB_REQUEST = 'SUBMIT_HP_JOB_REQUEST';
|
||||
export const SUBMIT_HP_JOB_SUCCESS = 'SUBMIT_HP_JOB_SUCCESS';
|
||||
export const SUBMIT_HP_JOB_FAILURE = 'SUBMIT_HP_JOB_FAILURE';
|
||||
|
||||
export const submitHPJob = data => ({
|
||||
type: SUBMIT_HP_JOB_REQUEST,
|
||||
data,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_KIND_HP = 'CHANGE_MC_KIND_HP';
|
||||
|
||||
export const changeMCKindHP = kind => ({
|
||||
type: CHANGE_MC_KIND_HP,
|
||||
kind,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_FILE_SYSTEM_HP = 'CHANGE_MC_FILE_SYSTEM_HP';
|
||||
|
||||
export const changeMCFileSystemHP = (kind, path) => ({
|
||||
type: CHANGE_MC_FILE_SYSTEM_HP,
|
||||
kind,
|
||||
path,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_HTTP_GET_HP = 'CHANGE_MC_HTTP_GET_HP';
|
||||
|
||||
export const changeMCHttpGetHP = (port, path, scheme, host) => ({
|
||||
type: CHANGE_MC_HTTP_GET_HP,
|
||||
port,
|
||||
path,
|
||||
scheme,
|
||||
host,
|
||||
});
|
||||
|
||||
export const ADD_MC_HTTP_GET_HEADER_HP = 'ADD_MC_HTTP_GET_HEADER_HP';
|
||||
|
||||
export const addMCHttpGetHeaderHP = () => ({
|
||||
type: ADD_MC_HTTP_GET_HEADER_HP,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_HTTP_GET_HEADER_HP = 'CHANGE_MC_HTTP_GET_HEADER_HP';
|
||||
|
||||
export const changeMCHttpGetHeaderHP = (fieldName, value, index) => ({
|
||||
type: CHANGE_MC_HTTP_GET_HEADER_HP,
|
||||
fieldName,
|
||||
value,
|
||||
index,
|
||||
});
|
||||
|
||||
export const DELETE_MC_HTTP_GET_HEADER_HP = 'DELETE_MC_HTTP_GET_HEADER_HP';
|
||||
|
||||
export const deleteMCHttpGetHeaderHP = index => ({
|
||||
type: DELETE_MC_HTTP_GET_HEADER_HP,
|
||||
index,
|
||||
});
|
||||
|
||||
export const ADD_MC_METRICS_FORMAT_HP = 'ADD_MC_METRICS_FORMAT_HP';
|
||||
|
||||
export const addMCMetricsFormatHP = () => ({
|
||||
type: ADD_MC_METRICS_FORMAT_HP,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_METRIC_FORMAT_HP = 'CHANGE_MC_METRIC_FORMAT_HP';
|
||||
|
||||
export const changeMCMetricsFormatHP = (format, index) => ({
|
||||
type: CHANGE_MC_METRIC_FORMAT_HP,
|
||||
format,
|
||||
index,
|
||||
});
|
||||
|
||||
export const DELETE_MC_METRIC_FORMAT_HP = 'DELETE_MC_METRIC_FORMAT_HP';
|
||||
|
||||
export const deleteMCMetricsFormatHP = index => ({
|
||||
type: DELETE_MC_METRIC_FORMAT_HP,
|
||||
index,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_CUSTOM_CONTAINER_HP = 'CHANGE_MC_CUSTOM_CONTAINER_HP';
|
||||
|
||||
export const changeMCCustomContainerHP = yamlContainer => ({
|
||||
type: CHANGE_MC_CUSTOM_CONTAINER_HP,
|
||||
yamlContainer,
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
export const FETCH_HP_JOB_INFO_REQUEST = 'FETCH_HP_JOB_INFO_REQUEST';
|
||||
export const FETCH_HP_JOB_INFO_SUCCESS = 'FETCH_HP_JOB_INFO_SUCCESS';
|
||||
export const FETCH_HP_JOB_INFO_FAILURE = 'FETCH_HP_JOB_INFO_FAILURE';
|
||||
|
||||
export const fetchHPJobInfo = (name, namespace) => ({
|
||||
type: FETCH_HP_JOB_INFO_REQUEST,
|
||||
name,
|
||||
namespace,
|
||||
});
|
||||
|
||||
export const FETCH_HP_JOB_TRIAL_INFO_REQUEST = 'FETCH_HP_JOB_TRIAL_INFO_REQUEST';
|
||||
export const FETCH_HP_JOB_TRIAL_INFO_SUCCESS = 'FETCH_HP_JOB_TRIAL_INFO_SUCCESS';
|
||||
export const FETCH_HP_JOB_TRIAL_INFO_FAILURE = 'FETCH_HP_JOB_TRIAL_INFO_FAILURE';
|
||||
|
||||
export const fetchHPJobTrialInfo = (trialName, namespace) => ({
|
||||
type: FETCH_HP_JOB_TRIAL_INFO_REQUEST,
|
||||
trialName,
|
||||
namespace,
|
||||
});
|
||||
|
||||
export const CLOSE_DIALOG_TRIAL = 'CLOSE_DIALOG_TRIAL';
|
||||
|
||||
export const closeDialogTrial = () => ({
|
||||
type: CLOSE_DIALOG_TRIAL,
|
||||
});
|
||||
@@ -1,282 +0,0 @@
|
||||
export const CHANGE_YAML_NAS = 'CHANGE_YAML_NAS';
|
||||
|
||||
export const changeYaml = yaml => ({
|
||||
type: CHANGE_YAML_NAS,
|
||||
payload: yaml,
|
||||
});
|
||||
|
||||
export const CHANGE_META_NAS = 'CHANGE_META_NAS';
|
||||
|
||||
export const changeMeta = (name, value) => ({
|
||||
type: CHANGE_META_NAS,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
|
||||
export const CHANGE_SPEC_NAS = 'CHANGE_SPEC_NAS';
|
||||
|
||||
export const changeSpec = (name, value) => ({
|
||||
type: CHANGE_SPEC_NAS,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
|
||||
export const CHANGE_OBJECTIVE_NAS = 'CHANGE_OBJECTIVE_NAS';
|
||||
|
||||
export const changeObjective = (name, value) => ({
|
||||
type: CHANGE_OBJECTIVE_NAS,
|
||||
name,
|
||||
value,
|
||||
});
|
||||
|
||||
export const ADD_METRICS_NAS = 'ADD_METRICS_NAS';
|
||||
|
||||
export const addMetrics = () => ({
|
||||
type: ADD_METRICS_NAS,
|
||||
});
|
||||
|
||||
export const DELETE_METRICS_NAS = 'DELETE_METRICS_NAS';
|
||||
|
||||
export const deleteMetrics = index => ({
|
||||
type: DELETE_METRICS_NAS,
|
||||
index,
|
||||
});
|
||||
|
||||
export const EDIT_METRICS_NAS = 'EDIT_METRICS_NAS';
|
||||
|
||||
export const editMetrics = (index, value) => ({
|
||||
type: EDIT_METRICS_NAS,
|
||||
index,
|
||||
value,
|
||||
});
|
||||
|
||||
export const CHANGE_METRIC_STRATEGY_NAS = 'CHANGE_METRIC_STRATEGY_NAS';
|
||||
|
||||
export const metricStrategyChange = (index, strategy) => ({
|
||||
type: CHANGE_METRIC_STRATEGY_NAS,
|
||||
index,
|
||||
strategy,
|
||||
});
|
||||
|
||||
export const CHANGE_ALGORITHM_NAME_NAS = 'CHANGE_ALGORITHM_NAME_NAS';
|
||||
|
||||
export const changeAlgorithmName = algorithmName => ({
|
||||
type: CHANGE_ALGORITHM_NAME_NAS,
|
||||
algorithmName,
|
||||
});
|
||||
|
||||
export const ADD_ALGORITHM_SETTING_NAS = 'ADD_ALGORITHM_SETTING_NAS';
|
||||
|
||||
export const addAlgorithmSetting = () => ({
|
||||
type: ADD_ALGORITHM_SETTING_NAS,
|
||||
});
|
||||
|
||||
export const CHANGE_ALGORITHM_SETTING_NAS = 'CHANGE_ALGORITHM_SETTING_NAS';
|
||||
|
||||
export const changeAlgorithmSetting = (index, field, value) => ({
|
||||
type: CHANGE_ALGORITHM_SETTING_NAS,
|
||||
field,
|
||||
value,
|
||||
index,
|
||||
});
|
||||
|
||||
export const DELETE_ALGORITHM_SETTING_NAS = 'DELETE_ALGORITHM_SETTING_NAS';
|
||||
|
||||
export const deleteAlgorithmSetting = index => ({
|
||||
type: DELETE_ALGORITHM_SETTING_NAS,
|
||||
index,
|
||||
});
|
||||
|
||||
export const EDIT_NUM_LAYERS = 'EDIT_NUM_LAYERS';
|
||||
|
||||
export const editNumLayers = value => ({
|
||||
type: EDIT_NUM_LAYERS,
|
||||
value,
|
||||
});
|
||||
|
||||
export const ADD_SIZE = 'ADD_SIZE';
|
||||
|
||||
export const addSize = sizeType => ({
|
||||
type: ADD_SIZE,
|
||||
sizeType,
|
||||
});
|
||||
|
||||
export const EDIT_SIZE = 'EDIT_SIZE';
|
||||
|
||||
export const editSize = (sizeType, index, value) => ({
|
||||
type: EDIT_SIZE,
|
||||
sizeType,
|
||||
index,
|
||||
value,
|
||||
});
|
||||
|
||||
export const DELETE_SIZE = 'DELETE_SIZE';
|
||||
|
||||
export const deleteSize = (sizeType, index) => ({
|
||||
type: DELETE_SIZE,
|
||||
sizeType,
|
||||
index,
|
||||
});
|
||||
|
||||
export const ADD_OPERATION = 'ADD_OPERATION';
|
||||
|
||||
export const addOperation = () => ({
|
||||
type: ADD_OPERATION,
|
||||
});
|
||||
|
||||
export const DELETE_OPERATION = 'DELETE_OPERATION';
|
||||
|
||||
export const deleteOperation = index => ({
|
||||
type: DELETE_OPERATION,
|
||||
index,
|
||||
});
|
||||
|
||||
export const CHANGE_OPERATION = 'CHANGE_OPERATION';
|
||||
|
||||
export const changeOperation = (index, value) => ({
|
||||
type: CHANGE_OPERATION,
|
||||
index,
|
||||
value,
|
||||
});
|
||||
|
||||
export const ADD_PARAMETER_NAS = 'ADD_PARAMETER_NAS';
|
||||
|
||||
export const addParameter = opIndex => ({
|
||||
type: ADD_PARAMETER_NAS,
|
||||
opIndex,
|
||||
});
|
||||
|
||||
export const CHANGE_PARAMETER_NAS = 'CHANGE_PARAMETER_NAS';
|
||||
|
||||
export const changeParameter = (opIndex, paramIndex, field, value) => ({
|
||||
type: CHANGE_PARAMETER_NAS,
|
||||
opIndex,
|
||||
paramIndex,
|
||||
field,
|
||||
value,
|
||||
});
|
||||
|
||||
export const DELETE_PARAMETER_NAS = 'DELETE_PARAMETER_NAS';
|
||||
|
||||
export const deleteParameter = (opIndex, paramIndex) => ({
|
||||
type: DELETE_PARAMETER_NAS,
|
||||
opIndex,
|
||||
paramIndex,
|
||||
});
|
||||
|
||||
export const ADD_LIST_PARAMETER_NAS = 'ADD_LIST_PARAMETER_NAS';
|
||||
|
||||
export const addListParameter = (opIndex, paramIndex) => ({
|
||||
type: ADD_LIST_PARAMETER_NAS,
|
||||
opIndex,
|
||||
paramIndex,
|
||||
});
|
||||
|
||||
export const EDIT_LIST_PARAMETER_NAS = 'EDIT_LIST_PARAMETER_NAS';
|
||||
|
||||
export const editListParameter = (opIndex, paramIndex, listIndex, value) => ({
|
||||
type: EDIT_LIST_PARAMETER_NAS,
|
||||
opIndex,
|
||||
paramIndex,
|
||||
listIndex,
|
||||
value,
|
||||
});
|
||||
|
||||
export const DELETE_LIST_PARAMETER_NAS = 'DELETE_LIST_PARAMETER_NAS';
|
||||
|
||||
export const deleteListParameter = (opIndex, paramIndex, listIndex) => ({
|
||||
type: DELETE_LIST_PARAMETER_NAS,
|
||||
opIndex,
|
||||
paramIndex,
|
||||
listIndex,
|
||||
});
|
||||
|
||||
export const SUBMIT_NAS_JOB_REQUEST = 'SUBMIT_NAS_JOB_REQUEST';
|
||||
export const SUBMIT_NAS_JOB_SUCCESS = 'SUBMIT_NAS_JOB_SUCCESS';
|
||||
export const SUBMIT_NAS_JOB_FAILURE = 'SUBMIT_NAS_JOB_FAILURE';
|
||||
|
||||
export const submitNASJob = data => ({
|
||||
type: SUBMIT_NAS_JOB_REQUEST,
|
||||
data,
|
||||
});
|
||||
|
||||
export const CLOSE_SNACKBAR = 'CLOSE_SNACKBAR';
|
||||
|
||||
export const closeSnackbar = () => ({
|
||||
type: CLOSE_SNACKBAR,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_KIND_NAS = 'CHANGE_MC_KIND_NAS';
|
||||
|
||||
export const changeMCKindNAS = kind => ({
|
||||
type: CHANGE_MC_KIND_NAS,
|
||||
kind,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_FILE_SYSTEM_NAS = 'CHANGE_MC_FILE_SYSTEM_NAS';
|
||||
|
||||
export const changeMCFileSystemNAS = (kind, path) => ({
|
||||
type: CHANGE_MC_FILE_SYSTEM_NAS,
|
||||
kind,
|
||||
path,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_HTTP_GET_NAS = 'CHANGE_MC_HTTP_GET_NAS';
|
||||
|
||||
export const changeMCHttpGetNAS = (port, path, scheme, host) => ({
|
||||
type: CHANGE_MC_HTTP_GET_NAS,
|
||||
port,
|
||||
path,
|
||||
scheme,
|
||||
host,
|
||||
});
|
||||
|
||||
export const ADD_MC_HTTP_GET_HEADER_NAS = 'ADD_MC_HTTP_GET_HEADER_NAS';
|
||||
|
||||
export const addMCHttpGetHeaderNAS = () => ({
|
||||
type: ADD_MC_HTTP_GET_HEADER_NAS,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_HTTP_GET_HEADER_NAS = 'CHANGE_MC_HTTP_GET_HEADER_NAS';
|
||||
|
||||
export const changeMCHttpGetHeaderNAS = (fieldName, value, index) => ({
|
||||
type: CHANGE_MC_HTTP_GET_HEADER_NAS,
|
||||
fieldName,
|
||||
value,
|
||||
index,
|
||||
});
|
||||
|
||||
export const DELETE_MC_HTTP_GET_HEADER_NAS = 'DELETE_MC_HTTP_GET_HEADER_NAS';
|
||||
|
||||
export const deleteMCHttpGetHeaderNAS = index => ({
|
||||
type: DELETE_MC_HTTP_GET_HEADER_NAS,
|
||||
index,
|
||||
});
|
||||
|
||||
export const ADD_MC_METRICS_FORMAT_NAS = 'ADD_MC_METRICS_FORMAT_NAS';
|
||||
|
||||
export const addMCMetricsFormatNAS = () => ({
|
||||
type: ADD_MC_METRICS_FORMAT_NAS,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_METRIC_FORMAT_NAS = 'CHANGE_MC_METRIC_FORMAT_NAS';
|
||||
|
||||
export const changeMCMetricsFormatNAS = (format, index) => ({
|
||||
type: CHANGE_MC_METRIC_FORMAT_NAS,
|
||||
format,
|
||||
index,
|
||||
});
|
||||
|
||||
export const DELETE_MC_METRIC_FORMAT_NAS = 'DELETE_MC_METRIC_FORMAT_NAS';
|
||||
|
||||
export const deleteMCMetricsFormatNAS = index => ({
|
||||
type: DELETE_MC_METRIC_FORMAT_NAS,
|
||||
index,
|
||||
});
|
||||
|
||||
export const CHANGE_MC_CUSTOM_CONTAINER_NAS = 'CHANGE_MC_CUSTOM_CONTAINER_NAS';
|
||||
|
||||
export const changeMCCustomContainerNAS = yamlContainer => ({
|
||||
type: CHANGE_MC_CUSTOM_CONTAINER_NAS,
|
||||
yamlContainer,
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
export const FETCH_NAS_JOB_INFO_REQUEST = 'FETCH_NAS_JOB_INFO_REQUEST';
|
||||
export const FETCH_NAS_JOB_INFO_SUCCESS = 'FETCH_NAS_JOB_INFO_SUCCESS';
|
||||
export const FETCH_NAS_JOB_INFO_FAILURE = 'FETCH_NAS_JOB_INFO_FAILURE';
|
||||
|
||||
export const fetchNASJobInfo = (experimentName, namespace) => ({
|
||||
type: FETCH_NAS_JOB_INFO_REQUEST,
|
||||
experimentName,
|
||||
namespace,
|
||||
});
|
||||
@@ -1,104 +0,0 @@
|
||||
export const CLOSE_DIALOG = 'CLOSE_DIALOG';
|
||||
|
||||
export const closeDialog = () => ({
|
||||
type: CLOSE_DIALOG,
|
||||
});
|
||||
|
||||
export const OPEN_DIALOG = 'OPEN_DIALOG';
|
||||
|
||||
export const openDialog = (
|
||||
dialogType,
|
||||
configMapNamespace = '',
|
||||
configMapName = '',
|
||||
configMapPath = '',
|
||||
templateYaml = '',
|
||||
) => ({
|
||||
type: OPEN_DIALOG,
|
||||
dialogType,
|
||||
configMapNamespace,
|
||||
configMapName,
|
||||
configMapPath,
|
||||
templateYaml,
|
||||
});
|
||||
|
||||
export const FETCH_TRIAL_TEMPLATES_REQUEST = 'FETCH_TRIAL_TEMPLATES_REQUEST';
|
||||
export const FETCH_TRIAL_TEMPLATES_SUCCESS = 'FETCH_TRIAL_TEMPLATES_SUCCESS';
|
||||
export const FETCH_TRIAL_TEMPLATES_FAILURE = 'FETCH_TRIAL_TEMPLATES_FAILURE';
|
||||
|
||||
export const fetchTrialTemplates = () => ({
|
||||
type: FETCH_TRIAL_TEMPLATES_REQUEST,
|
||||
});
|
||||
|
||||
export const ADD_TEMPLATE_REQUEST = 'ADD_TEMPLATE_REQUEST';
|
||||
export const ADD_TEMPLATE_SUCCESS = 'ADD_TEMPLATE_SUCCESS';
|
||||
export const ADD_TEMPLATE_FAILURE = 'ADD_TEMPLATE_FAILURE';
|
||||
|
||||
export const addTemplate = (
|
||||
updatedConfigMapNamespace,
|
||||
updatedConfigMapName,
|
||||
updatedConfigMapPath,
|
||||
updatedTemplateYaml,
|
||||
) => ({
|
||||
type: ADD_TEMPLATE_REQUEST,
|
||||
updatedConfigMapNamespace,
|
||||
updatedConfigMapName,
|
||||
updatedConfigMapPath,
|
||||
updatedTemplateYaml,
|
||||
});
|
||||
|
||||
export const EDIT_TEMPLATE_REQUEST = 'EDIT_TEMPLATE_REQUEST';
|
||||
export const EDIT_TEMPLATE_SUCCESS = 'EDIT_TEMPLATE_SUCCESS';
|
||||
export const EDIT_TEMPLATE_FAILURE = 'EDIT_TEMPLATE_FAILURE';
|
||||
|
||||
export const editTemplate = (
|
||||
updatedConfigMapNamespace,
|
||||
updatedConfigMapName,
|
||||
configMapPath,
|
||||
updatedConfigMapPath,
|
||||
updatedTemplateYaml,
|
||||
) => ({
|
||||
type: EDIT_TEMPLATE_REQUEST,
|
||||
updatedConfigMapNamespace,
|
||||
updatedConfigMapName,
|
||||
configMapPath,
|
||||
updatedConfigMapPath,
|
||||
updatedTemplateYaml,
|
||||
});
|
||||
|
||||
export const DELETE_TEMPLATE_REQUEST = 'DELETE_TEMPLATE_REQUEST';
|
||||
export const DELETE_TEMPLATE_SUCCESS = 'DELETE_TEMPLATE_SUCCESS';
|
||||
export const DELETE_TEMPLATE_FAILURE = 'DELETE_TEMPLATE_FAILURE';
|
||||
|
||||
export const deleteTemplate = (
|
||||
updatedConfigMapNamespace,
|
||||
updatedConfigMapName,
|
||||
updatedConfigMapPath,
|
||||
) => ({
|
||||
type: DELETE_TEMPLATE_REQUEST,
|
||||
updatedConfigMapNamespace,
|
||||
updatedConfigMapName,
|
||||
updatedConfigMapPath,
|
||||
});
|
||||
|
||||
export const CHANGE_TEMPLATE = 'CHANGE_TEMPLATE';
|
||||
|
||||
export const changeTemplate = (
|
||||
updatedConfigMapNamespace,
|
||||
updatedConfigMapName,
|
||||
updatedConfigMapPath,
|
||||
updatedTemplateYaml,
|
||||
) => ({
|
||||
type: CHANGE_TEMPLATE,
|
||||
updatedConfigMapNamespace,
|
||||
updatedConfigMapName,
|
||||
updatedConfigMapPath,
|
||||
updatedTemplateYaml,
|
||||
});
|
||||
|
||||
export const FILTER_TEMPLATES = 'FILTER_TEMPLATES';
|
||||
|
||||
export const filterTemplates = (filteredConfigMapNamespace, filteredConfigMapName) => ({
|
||||
type: FILTER_TEMPLATES,
|
||||
filteredConfigMapNamespace,
|
||||
filteredConfigMapName,
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user