Skip to content

Commit e5545be

Browse files
committed
AGENT-1120: Initial Assisted Disconnected UI
1 parent 2d74757 commit e5545be

37 files changed

+1013
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: Assisted Disconnected UI - Push to master or release tag
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
tags:
8+
- 'v*-dis'
9+
10+
env:
11+
QUAY_ORG: quay.io/edge-infrastructure
12+
QUAY_REPO: assisted-disconnected-ui
13+
14+
jobs:
15+
preflight-check:
16+
# Prevents running the workflow when a brand-new tag points to the same commit as the master branch
17+
runs-on: ubuntu-latest
18+
outputs:
19+
skip: ${{ steps.check.outputs.skip }}
20+
steps:
21+
- uses: actions/checkout@v3
22+
with:
23+
fetch-depth: 0
24+
25+
- name: Check if a tag points to the same commit as the master branch
26+
id: check
27+
run: |
28+
if [[ "${GITHUB_REF_TYPE}" == "tag" ]] && [[ "${GITHUB_SHA}" == "$(git rev-parse origin/master)" ]]; then
29+
skip=true
30+
else
31+
skip=false
32+
fi
33+
echo "skip=${skip}" >> $GITHUB_OUTPUT
34+
echo "skip=${skip}"
35+
36+
publish-assisted-disconnected-ui:
37+
needs: preflight-check
38+
if: needs.preflight-check.outputs.skip == 'false'
39+
runs-on: ubuntu-latest
40+
steps:
41+
- uses: actions/checkout@v3
42+
43+
- name: Generate AIUI_APP_VERSION
44+
run: |
45+
AIUI_APP_VERSION=latest
46+
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
47+
AIUI_APP_VERSION=${GITHUB_REF_NAME}
48+
fi
49+
echo "AIUI_APP_VERSION=${AIUI_APP_VERSION}" >> $GITHUB_ENV
50+
echo "AIUI_APP_VERSION=${AIUI_APP_VERSION}"
51+
52+
- name: Generate tags
53+
id: generate-tags
54+
run: |
55+
tags=( latest-${GITHUB_SHA} latest )
56+
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
57+
tags+=( ${GITHUB_REF_NAME} )
58+
if [[ ! "${GITHUB_REF_NAME}" =~ -cim$ ]]; then
59+
tags+=( staging-released )
60+
fi
61+
fi
62+
echo "tags=${tags[@]}" >> $GITHUB_OUTPUT
63+
echo "tags=${tags[@]}"
64+
65+
- name: Build
66+
id: build
67+
uses: redhat-actions/buildah-build@v2
68+
with:
69+
image: ${{ env.QUAY_REPO }}
70+
tags: ${{ steps.generate-tags.outputs.tags }}
71+
labels: |
72+
org.openshift-assisted.github.repository=${{ github.repository }}
73+
org.openshift-assisted.github.actor=${{ github.actor }}
74+
org.openshift-assisted.github.run_id=${{ github.run_id }}
75+
org.openshift-assisted.github.sha=${{ github.sha }}
76+
org.openshift-assisted.github.ref_name=${{ github.ref_name }}
77+
build-args: |
78+
AIUI_APP_VERSION=${{ env.AIUI_APP_VERSION }}
79+
AIUI_APP_GIT_SHA=${{ github.sha }}
80+
AIUI_APP_IMAGE_REPO=${{ env.QUAY_ORG }}/${{ env.QUAY_REPO }}
81+
containerfiles: apps/assisted-disconnected-ui/Containerfile
82+
context: .
83+
84+
- name: Push to Quay.io
85+
id: push
86+
uses: redhat-actions/push-to-registry@v2.7
87+
with:
88+
image: ${{ steps.build.outputs.image }}
89+
tags: ${{ steps.build.outputs.tags }}
90+
registry: ${{ env.QUAY_ORG }}
91+
username: ${{ secrets.QUAY_EDGE_INFRA_ROBOT_USERNAME }}
92+
password: ${{ secrets.QUAY_EDGE_INFRA_ROBOT_PASSWORD }}

.github/workflows/push-to-master.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
tags:
88
- 'v*'
99
- '!v*-cim'
10+
- '!v*-virt'
1011

1112
env:
1213
QUAY_ORG: quay.io/edge-infrastructure

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ package-lock.json
4242
# Ignored apps
4343
apps/*
4444
!apps/assisted-ui
45+
!apps/assisted-disconnected-ui
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
**/node_modules/
2+
**/build/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/** @type {import('eslint').ESLint.ConfigData} */
2+
module.exports = {
3+
overrides: [
4+
{
5+
files: ['./vite.config.ts'],
6+
extends: ['@openshift-assisted/eslint-config'],
7+
env: {
8+
browser: false,
9+
},
10+
parserOptions: {
11+
tsconfigRootDir: __dirname,
12+
},
13+
rules: {
14+
'no-console': 'off',
15+
},
16+
},
17+
{
18+
files: ['./src/**/*.{ts,tsx}'],
19+
extends: ['@openshift-assisted/eslint-config', 'plugin:react/jsx-runtime'],
20+
parserOptions: {
21+
tsconfigRootDir: __dirname,
22+
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
23+
},
24+
rules: {
25+
'no-restricted-imports': [
26+
'error',
27+
{
28+
paths: [
29+
{
30+
name: 'react-i18next',
31+
importNames: ['useTranslation'],
32+
message:
33+
'Import `useTranslation` from `lib/common/hooks/use-translation-wrapper.ts` instead',
34+
},
35+
{
36+
name: '@openshift-assisted/ui-lib',
37+
message: 'Import from `@openshift-assisted/ui-lib/ocm` instead',
38+
},
39+
{
40+
name: '@patternfly/react-icons',
41+
message:
42+
'Import using full path `@patternfly/react-icons/dist/js/icons/<icon>` instead',
43+
},
44+
{
45+
name: '@patternfly/react-tokens',
46+
message:
47+
'Import using full path `@patternfly/react-tokens/dist/js/<token>` instead',
48+
},
49+
],
50+
},
51+
],
52+
},
53+
},
54+
],
55+
};
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
FROM registry.access.redhat.com/ubi9/nodejs-18-minimal:latest as ui-build
2+
USER root
3+
RUN microdnf install -y rsync git
4+
5+
WORKDIR /app
6+
COPY --chown=1001:0 / /app
7+
RUN ls /app
8+
ENV NODE_OPTIONS='--max-old-space-size=8192'
9+
RUN git config --global --add safe.directory /app
10+
RUN npm install -g corepack@0.24.1
11+
RUN yarn install --immutable && yarn build:all
12+
13+
FROM registry.access.redhat.com/ubi9/go-toolset:1.21 as proxy-build
14+
WORKDIR /app
15+
COPY apps/assisted-disconnected-ui/proxy /app
16+
USER 0
17+
RUN go build
18+
19+
FROM registry.access.redhat.com/ubi9/ubi-micro
20+
COPY --from=ui-build /app/apps/assisted-disconnected-ui/build /app/proxy/dist
21+
COPY --from=proxy-build /app/assisted-disconnected-ui /app/proxy
22+
WORKDIR /app/proxy
23+
EXPOSE 8080
24+
CMD ./assisted-disconnected-ui
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# The Assisted Installer Disconnected User Interface
2+
3+
## Setting up a local dev-environment
4+
5+
- Start [assisted-service](https://github.com/openshift/assisted-service). You can run it
6+
[locally via podman](https://github.com/openshift/assisted-service/tree/master/deploy/podman)
7+
8+
- Start UI
9+
10+
```bash
11+
AIUI_APP_API_URL=<assised_service_url> yarn start:assisted_disconnected_ui
12+
```
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" href="/favicon.ico" />
6+
<link rel="stylesheet" href="/src/styles.css" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
<title>Assisted Installer</title>
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
<script type="module" src="/src/main.tsx"></script>
13+
</body>
14+
</html>
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"dependencies": {
3+
"@openshift-assisted/ui-lib": "workspace:*",
4+
"@openshift-console/dynamic-plugin-sdk": "0.0.3",
5+
"@patternfly/patternfly": "5.2.0",
6+
"@patternfly/react-code-editor": "5.2.0",
7+
"@patternfly/react-core": "5.2.0",
8+
"@patternfly/react-icons": "5.2.0",
9+
"@patternfly/react-styles": "5.2.0",
10+
"@patternfly/react-table": "5.2.0",
11+
"@patternfly/react-tokens": "5.2.0",
12+
"@reduxjs/toolkit": "^1.9.1",
13+
"@sentry/browser": "^7.119",
14+
"axios": ">=0.22.0 <2.0.0",
15+
"i18next": "^20.4.0",
16+
"i18next-browser-languagedetector": "^6.1.2",
17+
"lodash": "^4",
18+
"monaco-editor": "^0.44.0",
19+
"react": "^18.2.0",
20+
"react-dom": "^18.2.0",
21+
"react-i18next": "^11.11.4",
22+
"react-monaco-editor": "^0.55.0",
23+
"react-redux": "^8.0.5",
24+
"react-router-dom": "^5.3.3",
25+
"react-router-dom-v5-compat": "^6.21.2",
26+
"react-tagsinput": "^3.20",
27+
"redux": "^4",
28+
"uuid": "^8.1",
29+
"yup": "^1.4.0"
30+
},
31+
"description": "A stand-alone web UI for the disconnected environments",
32+
"devDependencies": {
33+
"@tsconfig/vite-react": "^1.0.1",
34+
"@types/react": "17.0.x",
35+
"@vitejs/plugin-react-swc": "^3.0.1",
36+
"concurrently": "^8.2.2",
37+
"nodemon": "^3.0.3",
38+
"vite": "^4.5.6",
39+
"vite-plugin-environment": "^1.1.3"
40+
},
41+
"overrides": {
42+
"@patternfly/react-core": {
43+
"attr-accept": "2.2.2"
44+
}
45+
},
46+
"engines": {
47+
"node": ">=14"
48+
},
49+
"license": "Apache-2.0",
50+
"name": "@openshift-assisted/assisted-disconnected-ui",
51+
"private": true,
52+
"scripts": {
53+
"build": "vite build -c vite.config.ts",
54+
"check_types": "yarn run -T tsc --noEmit",
55+
"clean": "yarn run -T rimraf node_modules build",
56+
"preview": "vite preview -c vite.config.ts",
57+
"serve": "concurrently \"vite serve -c vite.config.ts\" \"cd proxy && nodemon --watch './**/*' --exec 'go run' app.go --signal SIGTERM\"",
58+
"format": "yarn run -T prettier --cache --check . \"!build\"",
59+
"fix-code-style": "yarn lint --fix && yarn format --write",
60+
"lint": "yarn run -T eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache ."
61+
},
62+
"type": "module",
63+
"version": "1.0.0"
64+
}
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package main
2+
3+
import (
4+
"crypto/tls"
5+
"net/http"
6+
"time"
7+
8+
"github.com/gorilla/mux"
9+
10+
"github.com/openshift-assisted/assisted-disconnected-ui/bridge"
11+
"github.com/openshift-assisted/assisted-disconnected-ui/config"
12+
"github.com/openshift-assisted/assisted-disconnected-ui/log"
13+
"github.com/openshift-assisted/assisted-disconnected-ui/server"
14+
)
15+
16+
func main() {
17+
tlsConfig, err := bridge.GetTlsConfig()
18+
if err != nil {
19+
panic(err)
20+
}
21+
22+
log := log.InitLogs()
23+
router := mux.NewRouter()
24+
25+
apiHandler := bridge.NewAssistedAPIHandler(tlsConfig)
26+
router.PathPrefix("/api/{forward:.*}").Handler(apiHandler)
27+
28+
spa := server.SpaHandler{}
29+
router.PathPrefix("/").Handler(server.GzipHandler(spa))
30+
31+
var serverTlsconfig *tls.Config
32+
33+
if config.TlsKeyPath != "" && config.TlsCertPath != "" {
34+
cert, err := tls.LoadX509KeyPair(config.TlsCertPath, config.TlsKeyPath)
35+
if err != nil {
36+
panic(err)
37+
}
38+
serverTlsconfig = &tls.Config{
39+
Certificates: []tls.Certificate{cert},
40+
}
41+
42+
}
43+
44+
srv := &http.Server{
45+
Handler: router,
46+
Addr: config.BridgePort,
47+
WriteTimeout: 15 * time.Second,
48+
ReadTimeout: 15 * time.Second,
49+
}
50+
51+
log.Info("Proxy running at", config.BridgePort)
52+
53+
if serverTlsconfig != nil {
54+
srv.TLSConfig = serverTlsconfig
55+
log.Info("Running as HTTPS")
56+
log.Fatal(srv.ListenAndServeTLS("", ""))
57+
} else {
58+
log.Fatal(srv.ListenAndServe())
59+
}
60+
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package bridge
2+
3+
import (
4+
"crypto/tls"
5+
"crypto/x509"
6+
"errors"
7+
"os"
8+
9+
"github.com/openshift-assisted/assisted-disconnected-ui/config"
10+
log "github.com/sirupsen/logrus"
11+
)
12+
13+
func GetTlsConfig() (*tls.Config, error) {
14+
tlsConfig := &tls.Config{}
15+
16+
if config.ApiInsecure == "true" {
17+
log.Warn("Using InsecureSkipVerify for API communication")
18+
tlsConfig.InsecureSkipVerify = true
19+
}
20+
21+
_, err := os.Stat("../certs/ca.crt")
22+
if errors.Is(err, os.ErrNotExist) {
23+
return tlsConfig, nil
24+
}
25+
caCert, err := os.ReadFile("../certs/ca.crt")
26+
if err != nil {
27+
return nil, err
28+
}
29+
30+
caCertPool := x509.NewCertPool()
31+
caCertPool.AppendCertsFromPEM(caCert)
32+
33+
tlsConfig.RootCAs = caCertPool
34+
return tlsConfig, nil
35+
}

0 commit comments

Comments
 (0)