Merge remote-tracking branch 'origin/merge-master-to-epinio-dev' into epinio-dev
10
.drone.yml
|
|
@ -227,6 +227,8 @@ steps:
|
|||
- name: build
|
||||
pull: default
|
||||
image: node:14
|
||||
environment:
|
||||
EMBED_PKG: https://releases.rancher.com/harvester-ui/plugin/harvester-1.0.3.tar.gz
|
||||
commands:
|
||||
- ./scripts/build-embedded
|
||||
|
||||
|
|
@ -270,6 +272,8 @@ steps:
|
|||
- name: build
|
||||
pull: default
|
||||
image: node:14
|
||||
environment:
|
||||
EMBED_PKG: https://releases.rancher.com/harvester-ui/plugin/harvester-1.0.3.tar.gz
|
||||
commands:
|
||||
- ./scripts/build-embedded
|
||||
|
||||
|
|
@ -304,6 +308,8 @@ steps:
|
|||
- name: build
|
||||
pull: default
|
||||
image: node:14
|
||||
environment:
|
||||
EMBED_PKG: https://releases.rancher.com/harvester-ui/plugin/harvester-1.0.3.tar.gz
|
||||
commands:
|
||||
- ./scripts/build-embedded
|
||||
|
||||
|
|
@ -327,10 +333,10 @@ steps:
|
|||
# steps:
|
||||
# - name: fossa
|
||||
# image: rancher/drone-fossa:latest
|
||||
# failure: ignore
|
||||
# settings:
|
||||
# api_key:
|
||||
# from_secret: FOSSA_API_KEY
|
||||
# when:
|
||||
# instance:
|
||||
# - drone-publish.rancher.io
|
||||
|
||||
# - drone-publish.rancher.io
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
// root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true
|
||||
|
|
@ -181,5 +181,9 @@ module.exports = {
|
|||
rules: { 'jest/prefer-expect-assertions': 'off' },
|
||||
extends: ['plugin:jest/all']
|
||||
},
|
||||
{
|
||||
files: ['docusaurus/**/*.{js,ts}'],
|
||||
rules: { 'no-use-before-define': 'off' },
|
||||
},
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Report a bug with Rancher Dashboard
|
||||
title: "<bug title: summarize bug in one line>"
|
||||
labels: kind/bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
<!--------- For bugs and general issues --------->
|
||||
**Setup**
|
||||
- Rancher version:
|
||||
|
|
@ -19,11 +27,3 @@
|
|||
|
||||
**Additional context**
|
||||
<!--Add any other context about the problem here. -->
|
||||
|
||||
<!--------- For feature requests --------->
|
||||
**Detailed Description**
|
||||
<!--- Provide a detailed description of the change or addition you are proposing -->
|
||||
|
||||
**Context**
|
||||
<!--- Why is this change important to you? How would you use it? -->
|
||||
<!--- How can it benefit other users? -->
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest a new feature or enhancement for Rancher Dashboard
|
||||
title: "<feature title: summarize feature in one line>"
|
||||
labels: 'kind/enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: Question
|
||||
about: Question on Rancher Dashboard UI
|
||||
title: "<question title: summarize question in one line>"
|
||||
labels: kind/question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ Fixes #
|
|||
|
||||
### Areas or cases that should be tested
|
||||
<!-- Areas that should be tested can include Airgap checks, Rancher upgrades, K8s upgrade, etc. -->
|
||||
<!-- Which browser did you use for local testing? The reviewer should test with a different browser. -->
|
||||
<!-- Add missing steps or rewrite them if have been missed or to complement existing information. This should define a clear way to reproduce it and not an approximation. -->
|
||||
|
||||
### Areas which could experience regressions
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
name: Publish Docusaurus
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Publish Docusaurus
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: cd docusaurus/ && yarn install --frozen-lockfile
|
||||
- name: Build website
|
||||
run: cd docusaurus/ && yarn build
|
||||
|
||||
# Popular action to deploy to GitHub Pages:
|
||||
# Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus
|
||||
- name: Publish Docusaurus
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
deploy_key: ${{ secrets.GH_PAGES_DEPLOY_KEY }}
|
||||
# Build output to publish to the `gh-pages` branch:
|
||||
publish_dir: ./docusaurus/build
|
||||
# The following lines assign commit authorship to the official
|
||||
# GH-Actions bot for deploys to `gh-pages` branch:
|
||||
# https://github.com/actions/checkout/issues/13#issuecomment-724415212
|
||||
user_name: github-actions[bot]
|
||||
user_email: 41898282+github-actions[bot]@users.noreply.github.com
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
name: Build and Publish Rancher Components
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'components-v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
scope: '@rancher'
|
||||
|
||||
- name: Install
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Lint
|
||||
run: yarn lint:lib
|
||||
|
||||
- name: Build
|
||||
run: yarn build:lib
|
||||
|
||||
- name: Unit Test
|
||||
run: yarn test:ci ./pkg/rancher-components
|
||||
|
||||
- name: Publish to npm
|
||||
run: yarn publish:lib
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
|
||||
- name: Install & Build
|
||||
run:
|
||||
RANCHER_ENV=epinio EXCLUDES_PKG=rancher-components ./.github/workflows/scripts/build-dashboard.sh
|
||||
RANCHER_ENV=epinio EXCLUDES_PKG=rancher-components,harvester EXCLUDES_NUXT_PLUGINS=plugins/plugin,plugins/version ./.github/workflows/scripts/build-dashboard.sh
|
||||
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@v2
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ echo "ROUTER_BASE: $ROUTER_BASE"
|
|||
echo
|
||||
echo "RANCHER_ENV: $RANCHER_ENV"
|
||||
echo "EXCLUDES_PKG: $EXCLUDES_PKG"
|
||||
echo "EXCLUDES_NUXT_PLUGINS: $EXCLUDES_NUXT_PLUGINS"
|
||||
echo
|
||||
echo "RELEASE_DIR: $RELEASE_DIR"
|
||||
RELEASE_LOCATION="$RELEASE_DIR/$ARTIFACT_NAME"
|
||||
|
|
@ -28,7 +29,7 @@ echo Installing dependencies
|
|||
yarn install --frozen-lockfile
|
||||
|
||||
echo Building
|
||||
NUXT_ENV_commit=$GITHUB_SHA NUXT_ENV_version=$GITHUB_REF_NAME OUTPUT_DIR="$ARTIFACT_LOCATION" ROUTER_BASE="$ROUTER_BASE" RANCHER_ENV=$RANCHER_ENV API=$API RESOURCE_BASE=$RESOURCE_BASE EXCLUDES_PKG=$EXCLUDES_PKG yarn run build --spa
|
||||
NUXT_ENV_commit=$GITHUB_SHA NUXT_ENV_version=$GITHUB_REF_NAME OUTPUT_DIR="$ARTIFACT_LOCATION" ROUTER_BASE="$ROUTER_BASE" RANCHER_ENV=$RANCHER_ENV API=$API RESOURCE_BASE=$RESOURCE_BASE EXCLUDES_PKG=$EXCLUDES_PKG EXCLUDES_NUXT_PLUGINS=$EXCLUDES_NUXT_PLUGINS yarn run build --spa
|
||||
|
||||
echo Creating tar
|
||||
tar -czf $RELEASE_LOCATION.tar.gz -C $ARTIFACT_LOCATION .
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
name: Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 'release-*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
|
@ -10,6 +14,7 @@ on:
|
|||
description: 'Environment to run tests against'
|
||||
type: environment
|
||||
required: true
|
||||
|
||||
env:
|
||||
TEST_USERNAME: admin
|
||||
TEST_PASSWORD: password
|
||||
|
|
@ -18,17 +23,18 @@ env:
|
|||
API: https://127.0.0.1
|
||||
TEST_PROJECT_ID: rancher-dashboard
|
||||
CYPRESS_API_URL: http://139.59.134.103:1234/
|
||||
TEST_RUN_ID: ${{github.event.pull_request.title}}_${{github.run_number}}_id_${{github.run_id}}
|
||||
TEST_RUN_ID: ${{github.run_number}}-${{github.run_attempt}}-${{github.event.pull_request.title || github.event.head_commit.message}}
|
||||
CYPRESS_coverage: true
|
||||
|
||||
jobs:
|
||||
e2e-test:
|
||||
if: "!contains( github.event.pull_request.labels.*.name, 'ci/skip-e2e')"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
|
||||
|
|
@ -39,31 +45,31 @@ jobs:
|
|||
run: yarn e2e:pre-prod
|
||||
|
||||
- name: Run tests
|
||||
run: yarn e2e:prod
|
||||
run: |
|
||||
yarn e2e:prod
|
||||
mkdir -p coverage-artifacts/coverage
|
||||
cp coverage/e2e/coverage-final.json coverage-artifacts/coverage/coverage-e2e.json
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{github.run_number}}-${{github.run_attempt}}-coverage
|
||||
path: coverage-artifacts/**/*
|
||||
|
||||
- name: Upload screenshots
|
||||
uses: actions/upload-artifact@v2
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
name: run_${{github.run_number}}_id_${{github.run_id}}_screenshots
|
||||
name: ${{github.run_number}}-${{github.run_attempt}}-screenshots
|
||||
path: cypress/screenshots
|
||||
|
||||
# Disabled due freezing issues related to the CI machine resources
|
||||
# - name: Upload videos
|
||||
# uses: actions/upload-artifact@v2
|
||||
# if: ${{ failure() }}
|
||||
# with:
|
||||
# name: run_${{github.run_number}}_id_${{github.run_id}}_videos
|
||||
# path: cypress/videos
|
||||
|
||||
unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
|
||||
|
|
@ -71,22 +77,24 @@ jobs:
|
|||
run: yarn install
|
||||
|
||||
- name: Run tests
|
||||
run: yarn test:ci
|
||||
run: |
|
||||
yarn test:ci
|
||||
mkdir -p coverage-artifacts/coverage
|
||||
cp coverage/unit/coverage-final.json coverage-artifacts/coverage/coverage-unit.json
|
||||
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: run_${{github.run_number}}_id_${{github.run_id}}_coverage
|
||||
path: coverage/**/*
|
||||
name: ${{github.run_number}}-${{github.run_attempt}}-coverage
|
||||
path: coverage-artifacts/**/*
|
||||
|
||||
i18n:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
|
||||
|
|
@ -99,11 +107,10 @@ jobs:
|
|||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
|
||||
|
|
@ -112,3 +119,53 @@ jobs:
|
|||
|
||||
- name: Run linters
|
||||
run: yarn lint
|
||||
|
||||
coverage:
|
||||
if: "!contains( github.event.pull_request.labels.*.name, 'ci/skip-e2e')"
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- unit-test
|
||||
- e2e-test
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
|
||||
- name: Download Coverage Artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{github.run_number}}-${{github.run_attempt}}-coverage
|
||||
|
||||
- name: Merge coverage files
|
||||
run: |
|
||||
ls
|
||||
yarn coverage
|
||||
ls coverage
|
||||
|
||||
# Job is flaky now and then, but we do not want to interrupt the development flow
|
||||
- name: Upload unit test coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: unit
|
||||
files: ./coverage/coverage-unit.json
|
||||
fail_ci_if_error: false
|
||||
|
||||
- name: Upload e2e test coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: e2e
|
||||
files: ./coverage/coverage-e2e.json
|
||||
fail_ci_if_error: false
|
||||
|
||||
- name: Upload merged coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: merged
|
||||
files: ./coverage/coverage.json
|
||||
fail_ci_if_error: false
|
||||
|
|
|
|||
|
|
@ -109,4 +109,5 @@ shell/rancher-components
|
|||
|
||||
# standalone script
|
||||
scripts/standalone/ui
|
||||
scripts/standalone/cert
|
||||
scripts/standalone/cert
|
||||
scripts/standalone/node
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"reporter": [ "json", "text-summary"],
|
||||
"report-dir": "coverage/e2e"
|
||||
}
|
||||
|
|
@ -43,6 +43,9 @@ const storePlugin = {
|
|||
Vue.use(storePlugin);
|
||||
|
||||
export const parameters = {
|
||||
previewTabs: {
|
||||
canvas: { hidden: false },
|
||||
},
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
layout: 'centered',
|
||||
// viewMode: 'docs',
|
||||
|
|
@ -57,12 +60,12 @@ export const parameters = {
|
|||
dark: {
|
||||
...themes.dark,
|
||||
brandTitle: 'Rancher Storybook',
|
||||
brandImage: 'https://raw.githubusercontent.com/rancher/dashboard/master/assets/images/pl/dark/rancher-logo.svg'
|
||||
brandImage: '/dark/rancher-logo.svg'
|
||||
},
|
||||
light: {
|
||||
...themes.normal,
|
||||
brandTitle: 'Rancher Storybook',
|
||||
brandImage: 'https://raw.githubusercontent.com/rancher/dashboard/master/assets/images/pl/rancher-logo.svg'
|
||||
brandImage: '/rancher-logo.svg'
|
||||
},
|
||||
darkClass: 'theme-dark',
|
||||
lightClass: 'theme-light',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 637.2 93.8" style="enable-background:new 0 0 637.2 93.8;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0075A8;}
|
||||
.st1{fill:#f8f8f8;}
|
||||
.st2{fill:#CCCCCC;}
|
||||
.st3{fill:#B3B3B3;}
|
||||
.st4{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M200.7,20.6l-2.2-13c-0.7-4.2-2.3-7.6-3.6-7.6c-1.3,0-2.4,3.5-2.4,7.7v3.4c0,4.2-3.5,7.7-7.7,7.7h-3.4
|
||||
c-0.2,0-0.5,0-0.7,0v9.4c0.2,0,0.5,0,0.7,0h12.8C198.5,28.2,201.4,24.8,200.7,20.6"/>
|
||||
<path class="st0" d="M170,9.6h-20.8c-0.2,0-0.3,0-0.5,0h-21.3c-0.3,0-0.5,0-0.7,0.1v-2c0-4.2-1.1-7.7-2.4-7.7
|
||||
c-1.3,0-2.9,3.4-3.6,7.6l-2.2,13c-0.7,4.2,2.2,7.6,6.4,7.6h12.8c1.3,0,2.6-0.2,3.6-0.6c-0.4,2.2-2.3,3.8-4.6,3.8h-18
|
||||
c-2.9,0-5.1-2.6-4.6-5.5l1.8-10.9c0.5-2.9-1.7-5.5-4.6-5.5H22.1c-1.9,0-3.5,1.1-4.3,2.8L1,38c-0.3,0.4-0.3,1,0.1,1.4l3.3,3.9
|
||||
c0.4,0.5,1.1,0.6,1.6,0.2l11.4-9v54.8c0,2.6,2.1,4.7,4.7,4.7h25.4c2.6,0,4.7-2.1,4.7-4.7v-19c0-2.6,2.1-4.7,4.7-4.7h63.3
|
||||
c2.6,0,4.7,2.1,4.7,4.7v19c0,2.6,2.1,4.7,4.7,4.7h25.4c2.6,0,4.7-2.1,4.7-4.7V68.6h-13.5c-4.2,0-7.7-3.5-7.7-7.7V47.8
|
||||
c0-2.5,1.2-4.7,3.1-6.1v15.7c0,4.2,3.5,7.7,7.7,7.7H170c4.2,0,7.7-3.5,7.7-7.7v-40C177.7,13.1,174.2,9.6,170,9.6"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M217.7,12.2h28.6c13.6,0,22.7,6.6,22.7,19.3c0,10.4-7.1,16.6-14,18.8c2.1,1.7,3.6,4.1,4.8,6.6
|
||||
c2.9,5.9,4.8,12.3,10.9,12.3c1.5,0,2.8-0.5,2.8-0.5L272.1,81c0,0-3.7,0.9-6.9,0.9c-8.2,0-12.9-3.2-17.8-14.2
|
||||
c-2.1-4.9-4.9-13.6-8.7-13.6h-3.9v27.4h-17.2V12.2z M234.9,24.7v17h6.2c4.9,0,10.7-1.5,10.7-8.9c0-6.1-3.9-8.1-8.6-8.1H234.9z"/>
|
||||
<path class="st1" d="M292.2,12.2h17.4l23.4,69.3h-17.4l-4.3-13.1h-23.2l-4.2,13.1h-15L292.2,12.2z M292.2,55.7h14.9l-4.7-14.6
|
||||
c-1.6-4.9-2.4-11.9-2.4-11.9h-0.4c0,0-1,7.1-2.6,11.8L292.2,55.7z"/>
|
||||
<path class="st1" d="M333.6,12.2H352l18.2,34.4c1.5,3,3.6,8.2,5.2,12.3h0.4c-0.2-3.9-0.7-9.4-0.7-13.5V12.2h14.1v69.3h-18
|
||||
l-18.9-34.3c-1.7-3.1-3.6-7.6-5-11.3h-0.4c0.3,3.9,0.8,8.5,0.8,12.3v33.3h-14.1V12.2z"/>
|
||||
<path class="st1" d="M392.9,46.5c0-25.1,12.4-35.4,31.6-35.4c20.2,0,28.3,11.1,26.1,26.1l-16,1.4c1.7-11-2.6-15.4-10.4-15.4
|
||||
c-7.5,0-13.6,5.5-13.6,23.4c0,18.9,6.6,23.7,14.1,23.7c7.1,0,12.8-4.4,11.6-13.5l15,1.5c1.1,14.1-9,24.4-27.5,24.4
|
||||
C405,82.8,392.9,71.7,392.9,46.5"/>
|
||||
<polygon class="st1" points="454.8,12.2 472,12.2 472,38.4 493,38.4 493,12.2 510.2,12.2 510.2,81.6 493,81.6 493,52.2 472,52.2
|
||||
472,81.6 454.8,81.6 "/>
|
||||
<polygon class="st1" points="517.1,12.2 563,12.2 563,25 534.2,25 534.2,39.9 558.3,39.9 558.3,52.6 534.2,52.6 534.2,68.8
|
||||
564,68.8 564,81.6 517.1,81.6 "/>
|
||||
<path class="st1" d="M567.3,12.2h28.6c13.6,0,22.7,6.6,22.7,19.3c0,10.4-7.1,16.6-14,18.8c2.1,1.7,3.6,4.1,4.8,6.6
|
||||
c2.9,5.9,4.8,12.3,10.9,12.3c1.5,0,2.8-0.5,2.8-0.5L621.7,81c0,0-3.7,0.9-6.9,0.9c-8.2,0-12.9-3.2-17.8-14.2
|
||||
c-2.1-4.9-4.9-13.6-8.7-13.6h-3.9v27.4h-17.2V12.2z M584.4,24.7v17h6.2c4.9,0,10.7-1.5,10.7-8.9c0-6.1-3.9-8.1-8.6-8.1H584.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M625.2,16.6c0-3.9,2.9-5.6,5.6-5.6c2.7,0,5.6,1.7,5.6,5.6c0,3.8-2.9,5.5-5.6,5.5
|
||||
C628.2,22.2,625.2,20.5,625.2,16.6z M635.1,16.6c0-3.1-2-4.4-4.2-4.4c-2.2,0-4.3,1.3-4.3,4.4c0,3,2.1,4.4,4.3,4.4
|
||||
C633.1,21,635.1,19.7,635.1,16.6z M629,13.8h2c1,0,2,0.3,2,1.7c0,0.8-0.6,1.3-1.3,1.5l1.3,2.3h-1.2l-1.2-2.2h-0.5v2.2H629V13.8z
|
||||
M631.1,16.3c0.5,0,0.9-0.3,0.9-0.8c0-0.6-0.5-0.7-0.9-0.7h-1v1.5H631.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
|
@ -0,0 +1,51 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 637.2 93.8" style="enable-background:new 0 0 637.2 93.8;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0075A8;}
|
||||
.st1{fill:#59595B;}
|
||||
.st2{fill:#CCCCCC;}
|
||||
.st3{fill:#B3B3B3;}
|
||||
.st4{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M200.7,20.6l-2.2-13c-0.7-4.2-2.3-7.6-3.6-7.6c-1.3,0-2.4,3.5-2.4,7.7v3.4c0,4.2-3.5,7.7-7.7,7.7h-3.4
|
||||
c-0.2,0-0.5,0-0.7,0v9.4c0.2,0,0.5,0,0.7,0h12.8C198.5,28.2,201.4,24.8,200.7,20.6"/>
|
||||
<path class="st0" d="M170,9.6h-20.8c-0.2,0-0.3,0-0.5,0h-21.3c-0.3,0-0.5,0-0.7,0.1v-2c0-4.2-1.1-7.7-2.4-7.7
|
||||
c-1.3,0-2.9,3.4-3.6,7.6l-2.2,13c-0.7,4.2,2.2,7.6,6.4,7.6h12.8c1.3,0,2.6-0.2,3.6-0.6c-0.4,2.2-2.3,3.8-4.6,3.8h-18
|
||||
c-2.9,0-5.1-2.6-4.6-5.5l1.8-10.9c0.5-2.9-1.7-5.5-4.6-5.5H22.1c-1.9,0-3.5,1.1-4.3,2.8L1,38c-0.3,0.4-0.3,1,0.1,1.4l3.3,3.9
|
||||
c0.4,0.5,1.1,0.6,1.6,0.2l11.4-9v54.8c0,2.6,2.1,4.7,4.7,4.7h25.4c2.6,0,4.7-2.1,4.7-4.7v-19c0-2.6,2.1-4.7,4.7-4.7h63.3
|
||||
c2.6,0,4.7,2.1,4.7,4.7v19c0,2.6,2.1,4.7,4.7,4.7h25.4c2.6,0,4.7-2.1,4.7-4.7V68.6h-13.5c-4.2,0-7.7-3.5-7.7-7.7V47.8
|
||||
c0-2.5,1.2-4.7,3.1-6.1v15.7c0,4.2,3.5,7.7,7.7,7.7H170c4.2,0,7.7-3.5,7.7-7.7v-40C177.7,13.1,174.2,9.6,170,9.6"/>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M217.7,12.2h28.6c13.6,0,22.7,6.6,22.7,19.3c0,10.4-7.1,16.6-14,18.8c2.1,1.7,3.6,4.1,4.8,6.6
|
||||
c2.9,5.9,4.8,12.3,10.9,12.3c1.5,0,2.8-0.5,2.8-0.5L272.1,81c0,0-3.7,0.9-6.9,0.9c-8.2,0-12.9-3.2-17.8-14.2
|
||||
c-2.1-4.9-4.9-13.6-8.7-13.6h-3.9v27.4h-17.2V12.2z M234.9,24.7v17h6.2c4.9,0,10.7-1.5,10.7-8.9c0-6.1-3.9-8.1-8.6-8.1H234.9z"/>
|
||||
<path class="st1" d="M292.2,12.2h17.4l23.4,69.3h-17.4l-4.3-13.1h-23.2l-4.2,13.1h-15L292.2,12.2z M292.2,55.7h14.9l-4.7-14.6
|
||||
c-1.6-4.9-2.4-11.9-2.4-11.9h-0.4c0,0-1,7.1-2.6,11.8L292.2,55.7z"/>
|
||||
<path class="st1" d="M333.6,12.2H352l18.2,34.4c1.5,3,3.6,8.2,5.2,12.3h0.4c-0.2-3.9-0.7-9.4-0.7-13.5V12.2h14.1v69.3h-18
|
||||
l-18.9-34.3c-1.7-3.1-3.6-7.6-5-11.3h-0.4c0.3,3.9,0.8,8.5,0.8,12.3v33.3h-14.1V12.2z"/>
|
||||
<path class="st1" d="M392.9,46.5c0-25.1,12.4-35.4,31.6-35.4c20.2,0,28.3,11.1,26.1,26.1l-16,1.4c1.7-11-2.6-15.4-10.4-15.4
|
||||
c-7.5,0-13.6,5.5-13.6,23.4c0,18.9,6.6,23.7,14.1,23.7c7.1,0,12.8-4.4,11.6-13.5l15,1.5c1.1,14.1-9,24.4-27.5,24.4
|
||||
C405,82.8,392.9,71.7,392.9,46.5"/>
|
||||
<polygon class="st1" points="454.8,12.2 472,12.2 472,38.4 493,38.4 493,12.2 510.2,12.2 510.2,81.6 493,81.6 493,52.2 472,52.2
|
||||
472,81.6 454.8,81.6 "/>
|
||||
<polygon class="st1" points="517.1,12.2 563,12.2 563,25 534.2,25 534.2,39.9 558.3,39.9 558.3,52.6 534.2,52.6 534.2,68.8
|
||||
564,68.8 564,81.6 517.1,81.6 "/>
|
||||
<path class="st1" d="M567.3,12.2h28.6c13.6,0,22.7,6.6,22.7,19.3c0,10.4-7.1,16.6-14,18.8c2.1,1.7,3.6,4.1,4.8,6.6
|
||||
c2.9,5.9,4.8,12.3,10.9,12.3c1.5,0,2.8-0.5,2.8-0.5L621.7,81c0,0-3.7,0.9-6.9,0.9c-8.2,0-12.9-3.2-17.8-14.2
|
||||
c-2.1-4.9-4.9-13.6-8.7-13.6h-3.9v27.4h-17.2V12.2z M584.4,24.7v17h6.2c4.9,0,10.7-1.5,10.7-8.9c0-6.1-3.9-8.1-8.6-8.1H584.4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M625.2,16.6c0-3.9,2.9-5.6,5.6-5.6c2.7,0,5.6,1.7,5.6,5.6c0,3.8-2.9,5.5-5.6,5.5
|
||||
C628.2,22.2,625.2,20.5,625.2,16.6z M635.1,16.6c0-3.1-2-4.4-4.2-4.4c-2.2,0-4.3,1.3-4.3,4.4c0,3,2.1,4.4,4.3,4.4
|
||||
C633.1,21,635.1,19.7,635.1,16.6z M629,13.8h2c1,0,2,0.3,2,1.7c0,0.8-0.6,1.3-1.3,1.5l1.3,2.3h-1.2l-1.2-2.2h-0.5v2.2H629V13.8z
|
||||
M631.1,16.3c0.5,0,0.9-0.3,0.9-0.8c0-0.6-0.5-0.7-0.9-0.7h-1v1.5H631.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
|
@ -17,20 +17,26 @@
|
|||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"cSpell.words": [
|
||||
"autoscroll",
|
||||
"cacerts",
|
||||
"chainable",
|
||||
"Codecov",
|
||||
"epinio",
|
||||
"hevi",
|
||||
"kube",
|
||||
"kubeconfig",
|
||||
"kubectl",
|
||||
"Kubernetes",
|
||||
"kubevirt",
|
||||
"nuxt",
|
||||
"overcommit",
|
||||
"prepending",
|
||||
"protip",
|
||||
"pvcs",
|
||||
"testid",
|
||||
"tolerations",
|
||||
"userpreferences",
|
||||
"virtualmachine",
|
||||
"vuex"
|
||||
"vuex",
|
||||
"whatsnew"
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ During the transition to the new folder structured in 2.6.5 required by the plug
|
|||
- Run the script `./scripts/rejig -d` to move folders to their old location and update imports again
|
||||
Use this to convert newer branches to the old format (possibly useful for branches)
|
||||
|
||||
For more information on plugins see [Plugins](./docs/developer/PLUGINS.md).
|
||||
For more information on plugins see [Plugins](./docusaurus/docs/guide/plugins.md).
|
||||
|
||||
## Running for Development
|
||||
This is what you probably want to get started.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { defineConfig } from 'cypress';
|
||||
// Required for env vars to be available in cypress
|
||||
require('dotenv').config();
|
||||
|
||||
/**
|
||||
|
|
@ -23,20 +24,33 @@ const getSpecPattern = (): string[] => {
|
|||
const baseUrl = (process.env.TEST_BASE_URL || 'https://localhost:8005').replace(/\/$/, '');
|
||||
|
||||
export default defineConfig({
|
||||
projectId: process.env.TEST_PROJECT_ID,
|
||||
defaultCommandTimeout: 60000,
|
||||
trashAssetsBeforeRuns: true,
|
||||
env: {
|
||||
projectId: process.env.TEST_PROJECT_ID,
|
||||
defaultCommandTimeout: 60000,
|
||||
trashAssetsBeforeRuns: true,
|
||||
env: {
|
||||
baseUrl,
|
||||
username: process.env.TEST_USERNAME,
|
||||
password: process.env.TEST_PASSWORD,
|
||||
bootstrapPassword: process.env.CATTLE_BOOTSTRAP_PASSWORD,
|
||||
coverage: false,
|
||||
codeCoverage: {
|
||||
exclude: [
|
||||
'cypress/**/*.*',
|
||||
'**/__tests__/**/*.*',
|
||||
'**/shell/scripts/**/*.*',
|
||||
],
|
||||
include: [
|
||||
'shell/**/*.{vue,ts,js}',
|
||||
'pkg/rancher-components/src/components/**/*.{vue,ts,js}',
|
||||
]
|
||||
},
|
||||
username: process.env.TEST_USERNAME,
|
||||
password: process.env.TEST_PASSWORD,
|
||||
bootstrapPassword: process.env.CATTLE_BOOTSTRAP_PASSWORD,
|
||||
},
|
||||
e2e: {
|
||||
// We've imported your old cypress plugins here.
|
||||
// You may want to clean this up later by importing these.
|
||||
setupNodeEvents(on, config) {
|
||||
return require('./cypress/plugins/index.ts')(on, config);
|
||||
// For more info: https://docs.cypress.io/guides/tooling/code-coverage
|
||||
require('@cypress/code-coverage/task')(on, config);
|
||||
|
||||
return config;
|
||||
},
|
||||
experimentalSessionAndOrigin: true,
|
||||
specPattern: getSpecPattern(),
|
||||
|
|
|
|||
|
|
@ -58,12 +58,4 @@ export default class BurgerMenuPo extends ComponentPo {
|
|||
clusters(): Cypress.Chainable {
|
||||
return this.self().find('.body .clusters .cluster.selector.option');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get i18n menu
|
||||
* @returns {Cypress.Chainable}
|
||||
*/
|
||||
localization(): Cypress.Chainable {
|
||||
return this.self().getId('locale-selector');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,6 @@ describe('Side Menu: main', () => {
|
|||
burgerMenuPo.clusters().should('exist');
|
||||
});
|
||||
|
||||
it('Can display the localization menu', () => {
|
||||
const burgerMenuPo = new BurgerMenuPo();
|
||||
|
||||
burgerMenuPo.localization().should('exist');
|
||||
});
|
||||
|
||||
it('Can display at least one menu category label', () => {
|
||||
const burgerMenuPo = new BurgerMenuPo();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
const { baseUrl } = Cypress.config();
|
||||
const clusterManagerPath = `${ baseUrl }/c/local/manager/provisioning.cattle.io.cluster`;
|
||||
const clusterRequestBase = `${ baseUrl }/v1/provisioning.cattle.io.clusters/fleet-default`;
|
||||
const timestamp = +new Date();
|
||||
const clusterNamePartial = `e2e-test-create`;
|
||||
const clusterName = `${ clusterNamePartial }-${ timestamp }`;
|
||||
const clusterNameImport = `${ clusterNamePartial }-${ timestamp }-import`;
|
||||
|
||||
describe('Cluster Manager', () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
});
|
||||
|
||||
describe('using RKE2', () => {
|
||||
describe('given custom selection', () => {
|
||||
it('can create new cluster', () => {
|
||||
cy.userPreferences();
|
||||
cy.visit(clusterManagerPath);
|
||||
cy.getId('cluster-manager-list-create').click();
|
||||
cy.getId('cluster-manager-create-rke-switch').click();
|
||||
cy.getId('cluster-manager-create-grid-2-0').click();
|
||||
cy.getId('name-ns-description-name').type(clusterName);
|
||||
cy.getId('rke2-custom-create-save').click();
|
||||
|
||||
cy.url().should('include', `${ clusterManagerPath }/fleet-default/${ clusterName }#registration`);
|
||||
});
|
||||
|
||||
it('can create new imported generic cluster', () => {
|
||||
cy.visit(clusterManagerPath);
|
||||
cy.getId('cluster-manager-list-import').click();
|
||||
cy.getId('cluster-manager-create-grid-1-0').click();
|
||||
cy.getId('name-ns-description-name').type(clusterNameImport);
|
||||
cy.getId('cluster-manager-import-save').click();
|
||||
|
||||
cy.url().should('include', `${ clusterManagerPath }/fleet-default/${ clusterNameImport }#registration`);
|
||||
});
|
||||
|
||||
it('can navigate to local cluster explore product', () => {
|
||||
const clusterName = 'local';
|
||||
|
||||
cy.visit(clusterManagerPath);
|
||||
// Click explore button for the cluster row within the table matching given name
|
||||
cy.contains(clusterName).parent().parent().parent()
|
||||
.within(() => cy.getId('cluster-manager-list-explore-management').click());
|
||||
|
||||
cy.url().should('include', `/c/${ clusterName }/explorer`);
|
||||
});
|
||||
|
||||
it(`can see cluster's details`, () => {
|
||||
cy.visit(clusterManagerPath);
|
||||
// Click action menu button for the cluster row within the table matching given name
|
||||
cy.contains(clusterName).parent().parent().parent()
|
||||
.within(() => cy.getId('-action-button', '$').click());
|
||||
cy.getId('action-menu-0-item').click();
|
||||
|
||||
cy.contains(`Custom - ${ clusterName }`).should('exist');
|
||||
});
|
||||
|
||||
it('can view cluster YAML editor', () => {
|
||||
cy.visit(clusterManagerPath);
|
||||
// Click action menu button for the cluster row within the table matching given name
|
||||
cy.contains(clusterName).parent().parent().parent()
|
||||
.within(() => cy.getId('-action-button', '$').click());
|
||||
cy.getId('action-menu-1-item').click();
|
||||
cy.getId('yaml-editor-code-mirror').contains(clusterName);
|
||||
});
|
||||
|
||||
it('can edit cluster and see changes afterwards', () => {
|
||||
cy.intercept('PUT', `${ clusterRequestBase }/${ clusterName }`).as('saveRequest');
|
||||
|
||||
cy.visit(clusterManagerPath);
|
||||
// Click action menu button for the cluster row within the table matching given name
|
||||
cy.contains(clusterName).parent().parent().parent()
|
||||
.within(() => cy.getId('-action-button', '$').click());
|
||||
cy.getId('action-menu-0-item').click();
|
||||
cy.getId('name-ns-description-description').type(clusterName);
|
||||
cy.getId('rke2-custom-create-save').click();
|
||||
|
||||
cy.wait('@saveRequest').then(() => {
|
||||
cy.visit(`${ clusterManagerPath }/fleet-default/${ clusterName }?mode=edit#basic`);
|
||||
cy.getId('name-ns-description-description').find('input').should('have.value', clusterName);
|
||||
});
|
||||
});
|
||||
it('can delete cluster', () => {
|
||||
cy.intercept('DELETE', `${ clusterRequestBase }/${ clusterName }`).as('deleteRequest');
|
||||
|
||||
cy.visit(clusterManagerPath);
|
||||
// Click action menu button for the cluster row within the table matching given name
|
||||
cy.contains(clusterName).as('rowCell').parent().parent()
|
||||
.parent()
|
||||
.within(() => cy.getId('-action-button', '$').click());
|
||||
cy.getId('action-menu-4-item').click();
|
||||
cy.getId('prompt-remove-input').type(clusterName);
|
||||
cy.getId('prompt-remove-confirm-button').click();
|
||||
|
||||
cy.wait('@deleteRequest').then(() => {
|
||||
cy.get('@rowCell').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
it('can delete multiple clusters', () => {
|
||||
cy.intercept('DELETE', `${ clusterRequestBase }/${ clusterNameImport }`).as('deleteRequest');
|
||||
|
||||
cy.visit(clusterManagerPath);
|
||||
// Get row from a given name
|
||||
cy.contains(clusterNameImport).as('rowCell')
|
||||
// Click checkbox for the cluster row within the table matching given name
|
||||
.parent().parent()
|
||||
.parent()
|
||||
.within(() => cy.getId('-checkbox', '$').click({ multiple: true }));
|
||||
// Single buttons are replaced with action menu on mobile
|
||||
cy.getId('sortable-table-promptRemove').click({ force: true });
|
||||
cy.get('@rowCell').then((row) => {
|
||||
// In the markdown we have ALWAYS whitespace
|
||||
cy.getId('prompt-remove-input').type(row.text().trim());
|
||||
});
|
||||
cy.getId('prompt-remove-confirm-button').click();
|
||||
|
||||
cy.wait('@deleteRequest').then(() => {
|
||||
cy.get('@rowCell').should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -37,6 +37,8 @@ describe('Rancher setup', () => {
|
|||
.should('eq', true);
|
||||
rancherSetupAuthVerify.submit();
|
||||
|
||||
cy.location('pathname', { timeout: 15000 }).should('include', '/home');
|
||||
|
||||
// TODO: This assertion is commented as it started to fail after rebasing and cannot be corrected as it's not possible to run Rancher locally
|
||||
// cy.wait('@firstLoginReq').then((login) => {
|
||||
// expect(login.response?.statusCode).to.equal(200);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
type Matcher = '$' | '^' | '~' | '*' | '';
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
declare namespace Cypress {
|
||||
interface Chainable {
|
||||
login(username?: string, password?: string, cacheSession?: boolean): Chainable<Element>;
|
||||
byLabel(label: string,): Chainable<Element>;
|
||||
|
||||
/**
|
||||
* Wrapper for cy.get() to simply define the data-testid value that allows you to pass a matcher to find the element.
|
||||
* @param id Value used for the data-testid attribute of the element.
|
||||
* @param matcher Matching character used for attribute value:
|
||||
* - `$`: Suffixed with this value
|
||||
* - `^`: Prefixed with this value
|
||||
* - `~`: Contains this value as whitespace separated words
|
||||
* - `*`: Contains this value
|
||||
*/
|
||||
getId(id: string, matcher?: Matcher): Chainable<Element>;
|
||||
|
||||
/**
|
||||
* Wrapper for cy.find() to simply define the data-testid value that allows you to pass a matcher to find the element.
|
||||
* @param id Value used for the data-testid attribute of the element.
|
||||
* @param matcher Matching character used for attribute value:
|
||||
* - `$`: Suffixed with this value
|
||||
* - `^`: Prefixed with this value
|
||||
* - `~`: Contains this value as whitespace separated words
|
||||
* - `*`: Contains this value
|
||||
*/
|
||||
findId(id: string, matcher?: Matcher): Chainable<Element>;
|
||||
|
||||
/**
|
||||
* Override user preferences to default values, allowing to pass custom preferences for a deterministic scenario
|
||||
* Leave empty for reset to default values
|
||||
*/
|
||||
// eslint-disable-next-line no-undef
|
||||
userPreferences(preferences?: Partial<UserPreferences>): Chainable<null>;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/* eslint-disable no-console */
|
||||
/// <reference types="cypress" />
|
||||
require('dotenv').config();
|
||||
const { rmdir } = require('fs');
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
module.exports = (
|
||||
on: Cypress.PluginEvents,
|
||||
config: Cypress.PluginConfigOptions
|
||||
) => {
|
||||
return config;
|
||||
};
|
||||
|
||||
/**
|
||||
* Only upload videos for specs with failing
|
||||
* Run this function after every spec to delete passed tests video
|
||||
* https://docs.cypress.io/guides/guides/screenshots-and-videos#Only-upload-videos-for-specs-with-failing-or-retried-tests
|
||||
* TODO: Integrate this function when enabling videos once again with #6048
|
||||
*
|
||||
* @param on
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const deletePassedVideos = (on: Cypress.PluginEvents) => {
|
||||
on('after:spec', (_, results) => {
|
||||
// console.log(results);
|
||||
if (results && results.video) {
|
||||
const failures = results.tests.filter(({ state }) => state === 'failed');
|
||||
|
||||
if (!failures.length) {
|
||||
console.log('Deleting video for passed tests');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
rmdir(
|
||||
results.video,
|
||||
{ maxRetries: 10, recursive: true },
|
||||
(err: Error) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
return resolve();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { LoginPagePo } from '@/cypress/e2e/po/pages/login-page.po';
|
||||
import { Matcher } from '~/cypress/support/types';
|
||||
|
||||
/**
|
||||
* Login local authentication, including first login and bootstrap if not cached
|
||||
|
|
@ -49,11 +50,18 @@ Cypress.Commands.add('byLabel', (label) => {
|
|||
return cy.get('.labeled-input').contains(label).siblings('input');
|
||||
});
|
||||
|
||||
/**
|
||||
* Wrap the cy.find() command to simplify the selector declaration of the data-testid
|
||||
*/
|
||||
Cypress.Commands.add('findId', (id: string, matcher?: Matcher = '') => {
|
||||
return cy.find(`[data-testid${ matcher }="${ id }"]`);
|
||||
});
|
||||
|
||||
/**
|
||||
* Wrap the cy.get() command to simplify the selector declaration of the data-testid
|
||||
*/
|
||||
Cypress.Commands.add('getId', (id: string) => {
|
||||
return cy.get(`[data-testid="${ id }"]`);
|
||||
Cypress.Commands.add('getId', (id: string, matcher?: Matcher = '') => {
|
||||
return cy.get(`[data-testid${ matcher }="${ id }"]`);
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -66,7 +74,8 @@ Cypress.Commands.add('userPreferences', (preferences: Partial<UserPreferences> =
|
|||
statusCode: 201,
|
||||
body: {
|
||||
data: [{
|
||||
data: {
|
||||
type: 'userpreference',
|
||||
data: {
|
||||
'after-login-route': '\"home\"',
|
||||
cluster: 'local',
|
||||
'group-by': 'none',
|
||||
|
|
|
|||
|
|
@ -1,19 +1,6 @@
|
|||
|
||||
import '@cypress/code-coverage/support';
|
||||
import './commands';
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
login(username?: string, password?: string, cacheSession?: boolean): Chainable<Element>;
|
||||
byLabel(label: string,): Chainable<Element>;
|
||||
getId(id: string,): Chainable<Element>;
|
||||
// eslint-disable-next-line no-undef
|
||||
userPreferences(preferences?: Partial<UserPreferences>): Chainable<null>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO handle redirection errors better?
|
||||
// we see a lot of 'error navigation cancelled' uncaught exceptions that don't actually break anything; ignore them here
|
||||
Cypress.on('uncaught:exception', (err, runnable) => {
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
# Auth Providers
|
||||
|
||||
## Github
|
||||
|
||||
### Developer Set up
|
||||
Follow the in-dashboard instructions when configuring a Github auth provider.
|
||||
|
||||
### Multiple GitHub auth configs
|
||||
The auth system supports multiple GitHub auth URLs and using the appropriate one based on the Host header that a request comes in on. Configuring this is not exposed in the regular UI, but is particularly useful for development against a server that already has GitHub setup.
|
||||
|
||||
In `management.cattle.io.authconfig`, edit the `github` entry. Add a `hostnameToClientId` map of Host header value -> GitHub client ID:
|
||||
|
||||
```yaml
|
||||
hostnameToClientId:
|
||||
"localhost:8005": <your GitHub Client ID for localhost:8005>
|
||||
```
|
||||
|
||||
In the `secret`, namespace `cattle-global-data`, edit `githubconfig-clientsecret`. Add GitHub client ID -> base64-encoded client secret to the `data` section:
|
||||
|
||||
```yaml
|
||||
data:
|
||||
clientsecret: <the normal client secret already configured>
|
||||
<your client id>: <your base64-encoded client secret for localhost:8005>
|
||||
```
|
||||
|
||||
## Keycloak
|
||||
|
||||
### Developer Set Up (SAML)
|
||||
Use the steps below to set up a Keycloak instance for dev environments and configure an Auth Provider for it.
|
||||
|
||||
1. Bring up a local Keycloak instance in docker using the instructions at [here](https://www.keycloak.org/getting-started/getting-started-docker).
|
||||
|
||||
> Ensure that the admin user has a first name, last name and email. These fields are referenced in the Keycloak client's mappers which are then referenced in the Rancher's auth provider config.
|
||||
|
||||
> Double check the client has the correct checkboxes set, specifically the Mappers `group` entry.
|
||||
1. Using either the Ember or Vue UI set up the Keycloak auth provider by follow the instructions at [here](https://rancher.com/docs/rancher/v2.6/en/admin-settings/authentication/keycloak-saml/)
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Display Name Field | givenName |
|
||||
| User Name Field | email |
|
||||
| UID Field | email |
|
||||
| Groups Field| member |
|
||||
| Entity ID Field| Depending on Rancher API Url. For instance when running Dashboard locally `https://192.168.86.26:8005/v1-saml/keycloak/saml/metadata` |
|
||||
| Rancher API Host | Depending on Rancher API Url. For instance when running Dashboard locally `https://192.168.86.26:8005/`|
|
||||
| Private Key | For key and cert files, export the Client in the Keycloak UI via the `Clients` list page and extract & wrap the `saml.signing.certificate` and `saml.signing.private.key` as cert files (see [step 5](https://gist.github.com/PhilipSchmid/506b33cd74ddef4064d30fba50635c5b) for more info). |
|
||||
| Certificate | See Private Key section above|
|
||||
| Metadata | For the SAML Metadata, download as per Rancher docs. Be sure to follow the `NOTE` instructions regarding `EntitiesDescriptor` and `EntityDescriptor`. For a better set of instructions see [step 6](https://gist.github.com/PhilipSchmid/506b33cd74ddef4064d30fba50635c5b)|
|
||||
|
||||
### Developer Set Up (OIDC)
|
||||
1. In Vue UI set up the Keycloak OIDC provider with the following values
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Client ID | Find via the keycloak console |
|
||||
| Client Secret | Find via the keycloak console (client's credentials tab) |
|
||||
| Private Key (optional) | |
|
||||
| Certificate (optional) | |
|
||||
| Keycloak URL | URL of keycloak instance (no path) |
|
||||
| Keycloak Realm | Find via the keycloak console (above menu on left or in path after /realms/) |
|
||||
|
||||
> The user used when enabling the provider must be an Admin or in a group
|
||||
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# Directory Structure
|
||||
|
||||
The directory structure is mostly flat, with each top level dir being for a different important thing (or just required by Nuxt to be there).
|
||||
|
||||
|
||||
## Other commonly changed stuff
|
||||
|
||||
Path | Used for
|
||||
-----|---------
|
||||
assets | CSS, fonts, images, translations, etc resources which get processed during build
|
||||
components | All general components which don't have a separate special directory elsewhere
|
||||
layouts | The outermost components for rendering different kinds of pages (Nuxt)
|
||||
store | [Vuex](https://vuex.vuejs.org/) stores which maintain all the state for the life of a page load
|
||||
utils | Misc parsers, utilities, and other (usually) standalone code that doesn't fit anywhere else
|
||||
|
||||
## The rest
|
||||
These are mostly standard Nuxt dirs that you won't need to go into very often.
|
||||
|
||||
Path | Used for
|
||||
-----|---------
|
||||
static | Static files which get directly copied into the build with no processing
|
||||
middleware | Hooks called on every page load
|
||||
mixins | Code that is defined once and then applied to several different other components
|
||||
pages | The structure in here defines the routes that are available, and what gets rendered when one is hit
|
||||
plugins | Add-ons to modify vue/nuxt or load additional 3rd-party code. The "steve" API client also notably lives here.
|
||||
scripts | Shell scripts for building and related tasks, used by CI and `npm run ...` commands
|
||||
server | Server-side middleware and dev SSL cert
|
||||
test | Unit tests (or lack thereof)
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
# FOSSA
|
||||
|
||||
FOSSA is part of our efforts to ensure license compliance and scan for CVEs in dependencies. The results are published to a Fossa dashboard, you can request access to the Fossa dashboard via SUSE IT SD.
|
||||
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# Terminology
|
||||
|
||||
This is part of the developer [getting started guide](../../README.md).
|
||||
|
||||
The official Kubernetes [glossary](https://kubernetes.io/docs/reference/glossary/?fundamental=true) also explains Kubernetes-specific terminology.
|
||||
|
||||
| Term | Description |
|
||||
|-------|--------------|
|
||||
| Dashboard / Cluster Explorer / Vue UI | The application in this repository. It will slowly replace the older Ember UI |
|
||||
| Manager / Cluster Manager / Ember UI | The old [Ember based UI](https://github.com/rancher/ui) |
|
||||
| Norman | Old Rancher API which has mostly been superseded by Steve |
|
||||
| Steve | New Rancher API |
|
||||
| Rancher (Product) | A [Kubernetes Management Platform](https://rancher.com/products/rancher/). This product includes the Rancher API and UIs |
|
||||
| RKE | [Rancher Kubernetes Engine](https://rancher.com/products/rke/) - A certified Kubernetes distribution |
|
||||
| SSR | Server Side Rendering. Disabled by default when developing the Dashboard (enabled pre 2.6.6) |
|
||||
| SPA | Single Page Application. Enabled by default in production |
|
||||
| Vue | [A frontend client framework used by the Dashboard](https://vuejs.org/) |
|
||||
| Vuex | [Frontend state management](https://vuex.vuejs.org/) |
|
||||
| Nuxt | [Vue framework helper](https://nuxtjs.org/)|
|
||||
|
|
@ -1,309 +0,0 @@
|
|||
# Testing
|
||||
|
||||
## E2E Tests
|
||||
|
||||
This repo is configured for end-to-end testing with [Cypress](https://docs.cypress.io/api/table-of-contents) and the CI will run using a blank state of Rancher executed locally. The aim is however to enable also tests using remote instances of Ranchers.
|
||||
|
||||
Because of this, we extend the [Cypress best practices](https://docs.cypress.io/guides/references/best-practices#How-It-Works), so be sure to read them before write any test.
|
||||
|
||||
### Initial Setup
|
||||
|
||||
For the cypress test runner to consume the UI, you should specify the environment variables:
|
||||
|
||||
- Local authentication credentials
|
||||
- `TEST_USERNAME`, default `admin`
|
||||
- `TEST_PASSWORD`, user password or custom during first Rancher run
|
||||
- `CATTLE_BOOTSTRAP_PASSWORD`, initialization password which will also be used as `admin` password (do not pick `admin`)
|
||||
- `TEST_BASE_URL` // URL used by Cypress to run the tests, default `https://localhost:8005`
|
||||
- `TEST_SKIP_SETUP` // Avoid to execute bootstrap setup tests for already initialized Rancher instances
|
||||
- Dashboard
|
||||
- `TEST_PROJECT_ID` // Project ID used by Cypress/Sorry cypress to run the tests
|
||||
- `TEST_RUN_ID` (optional) // Identifier for your dashboard run, default value is timestamp
|
||||
|
||||
### Development with watch/dev
|
||||
|
||||
While writing the tests, you can simply run Rancher dashboard and then open the Cypress dashboard with the commands
|
||||
|
||||
- `yarn dev`
|
||||
- `yarn cy:open`
|
||||
|
||||
The Cypress dashboard will contain the options and the list of test suites. These will automatically re-run if they are altered (hot reloading).
|
||||
|
||||
For further information, consult [official documentation](https://docs.cypress.io/guides/guides/command-line#cypress-open).
|
||||
|
||||
### E2E Dashboard
|
||||
|
||||
#### Self-hosted: Sorry Cypress
|
||||
|
||||
Link to the dashboard: http://139.59.134.103:8080/
|
||||
|
||||
E2E tests can be added and displayed in a dashboard by defining the project ID with the env var `TEST_PROJECT_ID`, then run the script:
|
||||
|
||||
```bash
|
||||
yarn cy:run:sorry
|
||||
```
|
||||
|
||||
#### Cypress Dashboard
|
||||
|
||||
E2E tests can be displayed in Cypress dashboard by defining the project ID with the env var `TEST_PROJECT_ID`, then run the script by passing the parameters
|
||||
|
||||
```bash
|
||||
yarn cy:run --record --key YOUR_RECORD_KEY_HERE
|
||||
```
|
||||
|
||||
These values are provided when you create a new project within Cypress dashboard or within `Project settings`.
|
||||
|
||||
It's also possible to run a workflow in GitHub Actions E2E test using these values to record on personal dashboards.
|
||||
|
||||
### Local and CI/prod run
|
||||
|
||||
It is possible to start the project and run all the tests at once with a single command. There's however a difference between `dev` and `production` run. The first will not require an official certificate and will build the project in `.nuxt`, while the production will enable all the SSL configurations to run encrypted.
|
||||
|
||||
- `yarn e2e:pre-dev`, to optionally initialize Docker and build the project, if not already done
|
||||
- `yarn e2e:dev`, single run local development
|
||||
- `yarn e2e:pre-prod`, to optionally initialize Docker and build the project, required for GitHub Actions
|
||||
- `yarn e2e:dev`, for production use case and CI, which will also restart Docker and build the project
|
||||
|
||||
### Custom Commands
|
||||
|
||||
As Cypress common practice, some custom commands have been created within `command.ts` file to simplify the development process. Please consult Cypress documentation for more details about when and how to use them.
|
||||
|
||||
Worth mentioning the `cy.getId()` command, as it is mainly used to select elements. This would require to add `data-testid` to your element inside the markup.
|
||||
|
||||
### Writing tests
|
||||
|
||||
Test specs should be grouped logically, normally by page or area of the Dashboard but also by a specific feature or component.
|
||||
|
||||
Tests should make use of common Page Object (PO) components. These can be pages or individual components which expose a useful set of tools, but most importantly contain the selectors for the DOM elements that need to be used. These will ensure changes to the underlying components don't require a rewrite of many many tests. They also allow parent components to easily search for children (for example easily finding all anchors in a section instead of the whole page). Given that tests are typescript it should be easy to explore the functionality.
|
||||
|
||||
Some examples of PO functionality
|
||||
|
||||
```ts
|
||||
HomePage.gotTo()
|
||||
new HomePagePo().checkIsCurrentPage()
|
||||
new BurgerMenuPo().clusters()
|
||||
new AsyncButtonPO('[data-testid="my-button"]').isDisabled()
|
||||
new LoginPagePo().username().set('admin')
|
||||
```
|
||||
|
||||
POs all inherit a root `component.po`. Common component functionality can be added there. They also expose their core cypress (chainable) element.
|
||||
|
||||
There are a large number of pages and components in the Dashboard and only a small set of POs. These will be expanded as the tests grow.
|
||||
|
||||
Note: When selecting an element be sure to use the attribute `data-testid`, even in case of lists where elements are distinguished by an index suffix.
|
||||
|
||||
### Tips
|
||||
|
||||
The Cypress UI is very much your friend. There you can click pick tests to run, easily visually track the progress of the test, see the before/after state of each cypress command (specifically good for debugging failed steps), see https requests, etc.
|
||||
|
||||
Tests can also be restricted before cypress runs, or at run time, by prepending `.only` to the run.
|
||||
|
||||
```ts
|
||||
describe.only('Burger Side Nav Menu', () => {
|
||||
beforeEach
|
||||
```
|
||||
|
||||
```ts
|
||||
it.only('Opens and closes on menu icon click', () => {
|
||||
```
|
||||
|
||||
## Unit tests
|
||||
|
||||
The dashboard is configured to run unit tests with Jest in combination of vue-test-utils, for Vue scoped cases.
|
||||
|
||||
Requirements to accept tests:
|
||||
|
||||
- JS and TS formats
|
||||
- Suffix with `.test` or `.spec`
|
||||
- Contained in any directory `__tests__`
|
||||
|
||||
Adopted commands:
|
||||
|
||||
- `yarn test`, run and watch every test
|
||||
- `yarn test:ci`, script used for CI, which outputs a coverage report to `/coverage` folder
|
||||
|
||||
Example tests can be found in `/components/__tests__`. For more information about testing vue components, see the [vue test utils](https://vue-test-utils.vuejs.org/) and [jest](https://jestjs.io/docs/getting-started) docs.
|
||||
|
||||
### VSCode debugging tools
|
||||
|
||||
It is possible to use debugging tools within Jest via VSCode. To do so, open the debugger panel (Ctrl/Cmd+Shift+D) and select the `Debug Jest Tests` option from the dropdown. This will start a debug session with the Jest tests, allowing you to set breakpoint, inspect code and visualize variables on the panel itself. As usual it's possible to execute the tests by `F5` after selecting the right option.
|
||||
|
||||
### Style guide
|
||||
|
||||
On top of the recommendation provided by the [Vue documentation](https://vuejs.org/guide/scaling-up/testing.html), it is also encouraged to follow these patterns to create readable and aimed tests.
|
||||
|
||||
#### Describe and test/it statement
|
||||
|
||||
To clearly state the scope of the test, it's convenient to define in the first `describe` always define with a noun the name of the function, method, or component being tested. Multiple assertions may be grouped together under a common statement in `describe` block, as it helps to avoid repetition and ensure a set of tests to be included. Each `test`/`it` block should then start with a verb related to what is the expectation.
|
||||
|
||||
```ts
|
||||
describe('myfunction', () => {
|
||||
describe('given the same parameter', () => {
|
||||
it('should return the same result', () => {
|
||||
// Test code
|
||||
});
|
||||
|
||||
it('should return something else for a second parameter', () => {
|
||||
// Test code
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
For further information, consult the [Jest API](https://jestjs.io/docs/api#describename-fn) documentation.
|
||||
|
||||
#### Simple tests
|
||||
|
||||
Test with the highest readability and reliability should avoid logic, as this will increase line of code and have to be tested as well. Static data is then preferred over computation and should be declared always within the describe or test or as close as possible.
|
||||
|
||||
Don't:
|
||||
|
||||
```ts
|
||||
test('define if is required to use this in our component from the response', () => {
|
||||
const myData = externalFunction(externalData);
|
||||
|
||||
for(data in myData) {
|
||||
data.key = 'something else'
|
||||
}
|
||||
|
||||
expect(isRequired(myData)).toBe(true);
|
||||
});
|
||||
```
|
||||
|
||||
Do:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test('should return true', () => {
|
||||
const myData = { key: 'required case' };
|
||||
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
#### AAA pattern
|
||||
|
||||
Adoption of AAA format (arrange, act, assert) for tests.
|
||||
|
||||
- Arrange is where you prepare test, e.g. set properties to a component or declare variables
|
||||
- Act is when an event or function is triggered
|
||||
- Assertion correspond to the expectation of the test
|
||||
|
||||
Don't:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test('should return true', () => {
|
||||
let myData = { key: 'required case' };
|
||||
|
||||
expect(myData).toBeTruthy();
|
||||
|
||||
myData.key = 'something else';
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(true);
|
||||
|
||||
myData['key2'] = 'another key/value';
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Do:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test('should return true', () => {
|
||||
const myData = { key: 'required case' };
|
||||
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false if malformed data', () => {
|
||||
const myData = {
|
||||
key: 'required case',
|
||||
key2: 'another key/value'
|
||||
};
|
||||
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
#### Behaviors over implementations
|
||||
|
||||
As also defined in the Vue documentation for [component](https://vuejs.org/guide/scaling-up/testing.html#component-testing) and [composable testing](https://vuejs.org/guide/scaling-up/testing.html#testing-composables), it is recommended to test rendered elements over internal API of the component.
|
||||
|
||||
Following an input example as in the [documentation](https://v2.vuejs.org/v2/cookbook/unit-testing-vue-components.html?redirect=true#Base-Example).
|
||||
|
||||
Don't:
|
||||
|
||||
```ts
|
||||
const wrapper = mount(YourComponent);
|
||||
const inputWrapper = wrapper.find(`[data-testid=your-component]`;
|
||||
|
||||
inputWrapper.setValue(1);
|
||||
|
||||
expect(wrapper.emitted('input')[0][0]).toBe(1);
|
||||
```
|
||||
|
||||
Do:
|
||||
|
||||
```ts
|
||||
const wrapper = mount(YourComponent);
|
||||
const inputWrapper = wrapper.find(`[data-testid=your-component]`;
|
||||
|
||||
inputWrapper.setValue(1);
|
||||
|
||||
expect(wrapper.text()).toContain('1')
|
||||
```
|
||||
|
||||
#### Parameterization
|
||||
|
||||
When multiple cases are required to be tested for the same component, it is recommended to avoid multiple actions and assertions or even worse logic, but rather rely on Jest functions to parametrize the test.
|
||||
|
||||
Don't:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test('should return true', () => {
|
||||
let myData = { key: 'required case' };
|
||||
|
||||
expect(myData).toBeTruthy();
|
||||
|
||||
myData.key = 'something else';
|
||||
|
||||
expect(result).toBe(true);
|
||||
|
||||
myData.key = 'another value';
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Do:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test.each([
|
||||
'required case',
|
||||
'something else',
|
||||
'another value',
|
||||
])('should return true', (key) => {
|
||||
const myData = { key };
|
||||
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# Website
|
||||
|
||||
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
|
||||
|
||||
> Note: Docusaurus 2 expected node version `>=16.14`.
|
||||
|
||||
|
||||
### Installation
|
||||
|
||||
```
|
||||
$ yarn docs:install
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
> Note this command will open a web browser on the locally served site (http://localhost:3000)
|
||||
|
||||
```
|
||||
$ yarn docs:start
|
||||
```
|
||||
|
||||
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||
|
||||
> Note this command will open a web browser on the locally served site (http://localhost:3000)
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
$ yarn docs:build
|
||||
```
|
||||
|
||||
### Adding new documents
|
||||
|
||||
Guide for [sidebars.js](https://docusaurus.io/docs/sidebar).
|
||||
|
||||
> Note: We are using `sidebars.js` file to generated custome sidebar.
|
||||
|
||||
1. Create a Markdown file, greeting.md, and place it under the docs directory.
|
||||
|
||||
2. Add file name in `sidebars.js`.
|
||||
|
|
@ -0,0 +1 @@
|
|||
module.exports = { presets: [require.resolve('@docusaurus/core/lib/babel/preset')] };
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
# Auth Providers
|
||||
|
||||
## Github
|
||||
|
||||
### Developer Set up
|
||||
Follow the in-dashboard instructions when configuring a Github auth provider.
|
||||
|
||||
### Multiple GitHub auth configs
|
||||
The auth system supports multiple GitHub auth URLs and using the appropriate one based on the Host header that a request comes in on. Configuring this is not exposed in the regular UI, but is particularly useful for development against a server that already has GitHub setup.
|
||||
|
||||
In `management.cattle.io.authconfig`, edit the `github` entry. Add a `hostnameToClientId` map of Host header value -> GitHub client ID:
|
||||
|
||||
```yaml
|
||||
hostnameToClientId:
|
||||
"localhost:8005": <your GitHub Client ID for localhost:8005>
|
||||
```
|
||||
|
||||
In the `secret`, namespace `cattle-global-data`, edit `githubconfig-clientsecret`. Add GitHub client ID -> base64-encoded client secret to the `data` section:
|
||||
|
||||
```yaml
|
||||
data:
|
||||
clientsecret: <the normal client secret already configured>
|
||||
<your client id>: <your base64-encoded client secret for localhost:8005>
|
||||
```
|
||||
|
||||
## Keycloak
|
||||
|
||||
### Developer Set Up (SAML)
|
||||
Use the steps below to set up a Keycloak instance for dev environments and configure an Auth Provider for it.
|
||||
|
||||
1. Bring up a local Keycloak instance in docker using the instructions at [here](https://www.keycloak.org/getting-started/getting-started-docker).
|
||||
|
||||
> Ensure that the admin user has a first name, last name and email. These fields are referenced in the Keycloak client's mappers which are then referenced in the Rancher's auth provider config.
|
||||
|
||||
> Double check the client has the correct checkboxes set, specifically the Mappers `group` entry.
|
||||
1. Using either the Ember or Vue UI set up the Keycloak auth provider by follow the instructions at [here](https://rancher.com/docs/rancher/v2.6/en/admin-settings/authentication/keycloak-saml/)
|
||||
|
||||
| Field | Value |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Display Name Field | givenName |
|
||||
| User Name Field | email |
|
||||
| UID Field | email |
|
||||
| Groups Field | member |
|
||||
| Entity ID Field | Depending on Rancher API Url. For instance when running Dashboard locally `https://192.168.86.26:8005/v1-saml/keycloak/saml/metadata` |
|
||||
| Rancher API Host | Depending on Rancher API Url. For instance when running Dashboard locally `https://192.168.86.26:8005/` |
|
||||
| Private Key | For key and cert files, export the Client in the Keycloak UI via the `Clients` list page and extract & wrap the `saml.signing.certificate` and `saml.signing.private.key` as cert files (see [step 5](https://gist.github.com/PhilipSchmid/506b33cd74ddef4064d30fba50635c5b) for more info). |
|
||||
| Certificate | See Private Key section above |
|
||||
| Metadata | For the SAML Metadata, download as per Rancher docs. Be sure to follow the `NOTE` instructions regarding `EntitiesDescriptor` and `EntityDescriptor`. For a better set of instructions see [step 6](https://gist.github.com/PhilipSchmid/506b33cd74ddef4064d30fba50635c5b) |
|
||||
|
||||
### Developer Set Up (OIDC)
|
||||
|
||||
1. In Vue UI set up the Keycloak OIDC provider with the following values
|
||||
|
||||
| Field | Value |
|
||||
| ---------------------- | ---------------------------------------------------------------------------- |
|
||||
| Client ID | Find via the keycloak console |
|
||||
| Client Secret | Find via the keycloak console (client's credentials tab) |
|
||||
| Private Key (optional) | |
|
||||
| Certificate (optional) | |
|
||||
| Keycloak URL | URL of keycloak instance (no path) |
|
||||
| Keycloak Realm | Find via the keycloak console (above menu on left or in path after /realms/) |
|
||||
|
||||
> The user used when enabling the provider must be an Admin or in a group
|
||||
|
||||
|
|
@ -25,13 +25,13 @@ This is in contrast to the Norman API, which had a concept of 'actions' - impera
|
|||
|
||||
In a production setup these are all handled natively by Rancher. For development of dashboard, they are proxied to the Rancher install that the `API` environment variable points at.
|
||||
|
||||
Endpoint | Notes
|
||||
-------------------------|-------
|
||||
`/v3` | Norman API
|
||||
`/v3-public` | Norman unauthenticated API (mostly for info required to login)
|
||||
`/v1` | Steve API for the management ("local") cluster
|
||||
`/k8s/clusters/<id>` | Proxy straight to the native k8s API for the given downstream cluster
|
||||
`/k8s/clusters/<id>/v1` | Steve API for given downstream cluster via the server proxy
|
||||
| Endpoint | Notes |
|
||||
| ----------------------- | --------------------------------------------------------------------- |
|
||||
| `/v3` | Norman API |
|
||||
| `/v3-public` | Norman unauthenticated API (mostly for info required to login) |
|
||||
| `/v1` | Steve API for the management ("local") cluster |
|
||||
| `/k8s/clusters/<id>` | Proxy straight to the native k8s API for the given downstream cluster |
|
||||
| `/k8s/clusters/<id>/v1` | Steve API for given downstream cluster via the server proxy |
|
||||
|
||||
The older Norman API is served on `/v3`. The newer Steve API (see [here](https://github.com/rancher/api-spec/blob/master/specification.md) for spec) is served on `/v1` .
|
||||
|
||||
|
|
@ -104,29 +104,29 @@ There are 3 main stores for communicating with different parts of the Rancher AP
|
|||
|
||||
And then a bunch of others:
|
||||
|
||||
Name | For
|
||||
---------------------|-------
|
||||
action-menu | Maintains the current selection for tables and handling bulk operations on them
|
||||
auth | Authentication, logging in and out, etc
|
||||
catalog | Stores the index data for Helm catalogs and methods to find charts, determine if upgrades are available, etc
|
||||
github | Part of authentication, communicating with the GitHub API
|
||||
growl | Global "growl" notifications in the corner of the screen
|
||||
i18n | Internationalization
|
||||
index | The root store, manages things like which cluster you're connected to and what namespaces should be shown
|
||||
prefs | User preferences
|
||||
type-map | Meta-information about all the k8s types that are available to the current user and how they should be displayed
|
||||
wm | "Window manager" at the bottom of the screen for things like container shells and logs.
|
||||
| Name | For |
|
||||
| ----------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| action-menu | Maintains the current selection for tables and handling bulk operations on them |
|
||||
| auth | Authentication, logging in and out, etc |
|
||||
| catalog | Stores the index data for Helm catalogs and methods to find charts, determine if upgrades are available, etc |
|
||||
| github | Part of authentication, communicating with the GitHub API |
|
||||
| growl | Global "growl" notifications in the corner of the screen |
|
||||
| i18n | Internationalization |
|
||||
| index | The root store, manages things like which cluster you're connected to and what namespaces should be shown |
|
||||
| prefs | User preferences |
|
||||
| type-map | Meta-information about all the k8s types that are available to the current user and how they should be displayed |
|
||||
| wm | "Window manager" at the bottom of the screen for things like container shells and logs. |
|
||||
|
||||
|
||||
Store objects are accessed in different ways, below are common ways they are referenced by models and components
|
||||
|
||||
|Location|type|object|example|
|
||||
|----|----|----|----|
|
||||
| `/model/<resource type>` | Dispatching Actions | `this.$dispatch` | `this.$dispatch('cluster/find', { type: WORKLOAD_TYPES.JOB, id: relationship.toId }, { root: true })`
|
||||
| `/model/<resource type>` | Access getters (store type) | `this.$getters` | `this.$getters['schemaFor'](this.type)`
|
||||
| `/model/<resource type>` | Access getters (all) | `this.$rootGetters` | `this.$rootGetters['productId']`
|
||||
| component | Dispatching Actions | `this.$store.dispatch` | ``this.$store.dispatch(`${ inStore }/find`, { type: row.type, id: row.id })``
|
||||
| component | Access getters | `this.$store.getters` | `this.$store.getters['rancher/byId'](NORMAN.PRINCIPAL, this.value)`
|
||||
| Location | type | object | example |
|
||||
| ------------------------ | --------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `/model/<resource type>` | Dispatching Actions | `this.$dispatch` | `this.$dispatch('cluster/find', { type: WORKLOAD_TYPES.JOB, id: relationship.toId }, { root: true })` |
|
||||
| `/model/<resource type>` | Access getters (store type) | `this.$getters` | `this.$getters['schemaFor'](this.type)` |
|
||||
| `/model/<resource type>` | Access getters (all) | `this.$rootGetters` | `this.$rootGetters['productId']` |
|
||||
| component | Dispatching Actions | `this.$store.dispatch` | ``this.$store.dispatch(`${ inStore }/find`, { type: row.type, id: row.id })`` |
|
||||
| component | Access getters | `this.$store.getters` | `this.$store.getters['rancher/byId'](NORMAN.PRINCIPAL, this.value)` |
|
||||
|
||||
> Prefixing a property in a model with `$`, as per `model` rows above, results in calling properties on the store object directly.
|
||||
|
||||
|
|
@ -183,13 +183,13 @@ We also 'dehydrate' resources by stripping out properties with double underscore
|
|||
|
||||
Most of the options to create and fetch resources can be achieved via dispatching actions defined in `/plugins/dashboard-store/actions.js`
|
||||
|
||||
| Function | Action | Example Command | Description |
|
||||
|-------------|--------|-----------------|-----|
|
||||
| `create` | Create | `$store.$dispatch('<store type>/create', <new object>)`| Creates a new Proxy object of the required type (`type` property must be included in the new object) |
|
||||
| `clone` | Clone | `$store.$dispatch('<store type>/clone', { resource: <existing object> })` | Performs a deep clone and creates a proxy from it |
|
||||
| `findAll` | Fetch all of a resource type and watch for changes to the returned resources so that the list updates as it changes. | `$store.dispatch('<store type>/findAll', { type: <resource type> })` | Fetches all resources of the given type. Also, when applicable, will register the type for automatic updates. If the type has already been fetched return the local cached list instead |
|
||||
| `find` | Fetch a resource by ID and watch for changes to that individual resource. | `$store.dispatch('<store type>/find', { type: <resource type>, id: <resource id> })` | Finds the resource matching the ID. If the type has already been fetched return the local cached instance. |
|
||||
| `findMatching` | Fetch resources by label and watch for changes to the returned resources, a live array that updates as it changes. | `$store.dispatch('<store type>/findMatching', { type: <resource type>, selector: <label name:value map> })` | Fetches resources that have `metadata.labels` matching that of the name-value properties in the selector. Does not support match expressions. |
|
||||
| Function | Action | Example Command | Description |
|
||||
| -------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `create` | Create | `$store.$dispatch('<store type>/create', <new object>)` | Creates a new Proxy object of the required type (`type` property must be included in the new object) |
|
||||
| `clone` | Clone | `$store.$dispatch('<store type>/clone', { resource: <existing object> })` | Performs a deep clone and creates a proxy from it |
|
||||
| `findAll` | Fetch all of a resource type and watch for changes to the returned resources so that the list updates as it changes. | `$store.dispatch('<store type>/findAll', { type: <resource type> })` | Fetches all resources of the given type. Also, when applicable, will register the type for automatic updates. If the type has already been fetched return the local cached list instead |
|
||||
| `find` | Fetch a resource by ID and watch for changes to that individual resource. | `$store.dispatch('<store type>/find', { type: <resource type>, id: <resource id> })` | Finds the resource matching the ID. If the type has already been fetched return the local cached instance. |
|
||||
| `findMatching` | Fetch resources by label and watch for changes to the returned resources, a live array that updates as it changes. | `$store.dispatch('<store type>/findMatching', { type: <resource type>, selector: <label name:value map> })` | Fetches resources that have `metadata.labels` matching that of the name-value properties in the selector. Does not support match expressions. |
|
||||
|
||||
Once objects of most types are fetched they will be automatically updated. See [Watching Resources](#watching-resources) for more info. For some types this does not happen. For those cases, or when an immediate update is required, adding `force: true` to the `find` style actions will result in a fresh http request.
|
||||
|
||||
|
|
@ -285,22 +285,22 @@ The Create/Edit Yaml experience is controlled by `/components/ResourceYaml.vue`.
|
|||
|
||||
Special attention should be made of the `mode` and `as` params that's available via the `CreateEditView` mixin (as well as other helpful functionality). Changing these should change the behaviour of the resource details page (depending on the availability of resource type custom components).
|
||||
|
||||
For more information about CreateEditView and how to add new create/edit forms, see [Create/Edit Forms.](./forms-and-validation.md)
|
||||
For more information about CreateEditView and how to add new create/edit forms, see [Create/Edit Forms.](../guide/forms-and-validation.md)
|
||||
|
||||
| `mode` | `as` | Content |
|
||||
|------------|----------|-------|
|
||||
| falsy | falsy | Shows the View YAML or Customised Detail component|
|
||||
| falsy | `config` | Shows the View YAML or Customised Edit component (in read only mode)|
|
||||
| `edit` | falsy | Shows the Customised Edit component|
|
||||
| `edit` | `yaml` | Shows the Edit Yaml component|
|
||||
| `mode` | `as` | Content |
|
||||
| ------ | -------- | -------------------------------------------------------------------- |
|
||||
| falsy | falsy | Shows the View YAML or Customised Detail component |
|
||||
| falsy | `config` | Shows the View YAML or Customised Edit component (in read only mode) |
|
||||
| `edit` | falsy | Shows the Customised Edit component |
|
||||
| `edit` | `yaml` | Shows the Edit Yaml component |
|
||||
|
||||
In addition the Create process (assessable with the same url + `/create`) is also managed by the resource detail page with similar param options.
|
||||
|
||||
| `mode` | `as` | Content |
|
||||
|------------|----------|-------|
|
||||
| falsy | `yaml` | Show the Edit YAML component in create mode
|
||||
| `edit` | falsy | Show the Customised Edit component in create mode
|
||||
| `clone` | falsy | Shows the Customised Edit component in create mode pre-populated with an existing resource
|
||||
| `mode` | `as` | Content |
|
||||
| ------- | ------ | ------------------------------------------------------------------------------------------ |
|
||||
| falsy | `yaml` | Show the Edit YAML component in create mode |
|
||||
| `edit` | falsy | Show the Customised Edit component in create mode |
|
||||
| `clone` | falsy | Shows the Customised Edit component in create mode pre-populated with an existing resource |
|
||||
|
||||
### Customising Resource Detail Pages
|
||||
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# Auth Providers
|
||||
|
||||
## Github
|
||||
|
||||
### Developer Set up
|
||||
|
||||
Follow the in-dashboard instructions when configuring a Github auth provider.
|
||||
|
||||
### Multiple GitHub auth configs
|
||||
|
||||
The auth system supports multiple GitHub auth URLs and using the appropriate one based on the Host header that a request comes in on. Configuring this is not exposed in the regular UI, but is particularly useful for development against a server that already has GitHub setup.
|
||||
|
||||
In `management.cattle.io.authconfig`, edit the `github` entry. Add a `hostnameToClientId` map of Host header value -> GitHub client ID:
|
||||
|
||||
```yaml
|
||||
hostnameToClientId:
|
||||
"localhost:8005": <your GitHub Client ID for localhost:8005>
|
||||
```
|
||||
|
||||
In the `secret`, namespace `cattle-global-data`, edit `githubconfig-clientsecret`. Add GitHub client ID -> base64-encoded client secret to the `data` section:
|
||||
|
||||
```yaml
|
||||
data:
|
||||
clientsecret: <the normal client secret already configured>
|
||||
<your client id>: <your base64-encoded client secret for localhost:8005>
|
||||
```
|
||||
|
||||
## Keycloak
|
||||
|
||||
### Developer Set Up (SAML)
|
||||
|
||||
Use the steps below to set up a Keycloak instance for dev environments and configure an Auth Provider for it.
|
||||
|
||||
1. Bring up a local Keycloak instance in docker using the instructions at [here](https://www.keycloak.org/getting-started/getting-started-docker).
|
||||
|
||||
> Ensure that the admin user has a first name, last name and email. These fields are referenced in the Keycloak client's mappers which are then referenced in the Rancher's auth provider config.
|
||||
|
||||
> Double check the client has the correct checkboxes set, specifically the Mappers `group` entry.
|
||||
|
||||
1. Using either the Ember or Vue UI set up the Keycloak auth provider by follow the instructions at [here](https://rancher.com/docs/rancher/v2.6/en/admin-settings/authentication/keycloak-saml/)
|
||||
|
||||
| Field | Value |
|
||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Display Name Field | givenName |
|
||||
| User Name Field | email |
|
||||
| UID Field | email |
|
||||
| Groups Field | member |
|
||||
| Entity ID Field | Depending on Rancher API Url. For instance when running Dashboard locally `https://192.168.86.26:8005/v1-saml/keycloak/saml/metadata` |
|
||||
| Rancher API Host | Depending on Rancher API Url. For instance when running Dashboard locally `https://192.168.86.26:8005/` |
|
||||
| Private Key | For key and cert files, export the Client in the Keycloak UI via the `Clients` list page and extract & wrap the `saml.signing.certificate` and `saml.signing.private.key` as cert files (see [step 5](https://gist.github.com/PhilipSchmid/506b33cd74ddef4064d30fba50635c5b) for more info). |
|
||||
| Certificate | See Private Key section above |
|
||||
| Metadata | For the SAML Metadata, download as per Rancher docs. Be sure to follow the `NOTE` instructions regarding `EntitiesDescriptor` and `EntityDescriptor`. For a better set of instructions see [step 6](https://gist.github.com/PhilipSchmid/506b33cd74ddef4064d30fba50635c5b) |
|
||||
|
||||
### Developer Set Up (OIDC)
|
||||
|
||||
1. In Vue UI set up the Keycloak OIDC provider with the following values
|
||||
|
||||
| Field | Value |
|
||||
| ---------------------- | ---------------------------------------------------------------------------- |
|
||||
| Client ID | Find via the keycloak console |
|
||||
| Client Secret | Find via the keycloak console (client's credentials tab) |
|
||||
| Private Key (optional) | |
|
||||
| Certificate (optional) | |
|
||||
| Keycloak URL | URL of keycloak instance (no path) |
|
||||
| Keycloak Realm | Find via the keycloak console (above menu on left or in path after /realms/) |
|
||||
|
||||
> The user used when enabling the provider must be an Admin or in a group
|
||||
|
|
@ -14,7 +14,7 @@ This process cannot be reversed, still admin password can be changed in the prof
|
|||
Rancher uses the following cookies:
|
||||
|
||||
| Name | Description |
|
||||
|======|=============|
|
||||
|------|-------------|
|
||||
| `R_SESS` | The logged in user's session token |
|
||||
| `R_PCS` | The user's preferred color scheme, used for server side rendering. If it's auto, it's the user's system preference. |
|
||||
| `CSRF` | Cross site request. The server sets this cookie as defined in the header of the request. For any request other than a GET request, we have to use Javascript to read this value and use it as a header in the request. That proves we are using our code on Rancher's domain and our header matches that cookie. |
|
||||
|
|
@ -52,8 +52,7 @@ There are three Kubernetes resources in the local Rancher server Kubernetes clus
|
|||
|
||||
Rancher maintains a list of management clusters to maintain a consistent API for tracking all kinds of Kubernetes clusters, including imported clusters. There is one management Cluster resource for every downstream cluster managed by Rancher. It is also the Steve API's representation of Norman/v1 clusters.
|
||||
|
||||
In the UI, the management Cluster resource is used in Cluster Explorer. like the provisioning and CAPI cluster resources. It’s used to show info on Kubernetes version, cloud provider, node OSs, and resource usage. No, you can’t edit them, they’re available to users who can work within clusters but can’t necessarily provision clusters. RKE1 and RKE2 clusters both have management clusters; it’s like, a shared interface between all types of clusters so we can have a consistent experience within Cluster Explorer
|
||||
* v1
|
||||
In the UI, the management Cluster resource is used in Cluster Explorer. like the provisioning and CAPI cluster resources. It’s used to show info on Kubernetes version, cloud provider, node OSs, and resource usage. No, you can’t edit them, they’re available to users who can work within clusters but can’t necessarily provision clusters. RKE1 and RKE2 clusters both have management clusters; it’s like, a shared interface between all types of clusters so we can have a consistent experience within Cluster Explorer * v1
|
||||
|
||||
The `kubectl` command to get the management Cluster resources is:
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# Directory Structure
|
||||
|
||||
The directory structure is mostly flat, with each top level dir being for a different important thing (or just required by Nuxt to be there).
|
||||
|
||||
|
||||
## Other commonly changed stuff
|
||||
|
||||
| Path | Used for |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------- |
|
||||
| assets | CSS, fonts, images, translations, etc resources which get processed during build |
|
||||
| components | All general components which don't have a separate special directory elsewhere |
|
||||
| layouts | The outermost components for rendering different kinds of pages (Nuxt) |
|
||||
| store | [Vuex](https://vuex.vuejs.org/) stores which maintain all the state for the life of a page load |
|
||||
| utils | Misc parsers, utilities, and other (usually) standalone code that doesn't fit anywhere else |
|
||||
|
||||
## The rest
|
||||
These are mostly standard Nuxt dirs that you won't need to go into very often.
|
||||
|
||||
| Path | Used for |
|
||||
| ---------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| static | Static files which get directly copied into the build with no processing |
|
||||
| middleware | Hooks called on every page load |
|
||||
| mixins | Code that is defined once and then applied to several different other components |
|
||||
| pages | The structure in here defines the routes that are available, and what gets rendered when one is hit |
|
||||
| plugins | Add-ons to modify vue/nuxt or load additional 3rd-party code. The "steve" API client also notably lives here. |
|
||||
| scripts | Shell scripts for building and related tasks, used by CI and `npm run ...` commands |
|
||||
| server | Server-side middleware and dev SSL cert |
|
||||
| test | Unit tests (or lack thereof) |
|
||||
|
|
@ -10,16 +10,16 @@ The Rancher UI injects the following global values
|
|||
into the Helm chart values of apps installed through Rancher.
|
||||
It adds them under `values.global.cattle.`
|
||||
|
||||
| YAML Directive | Source |
|
||||
|------|-------|
|
||||
| `cattle.clusterId` | The management cluster ID |
|
||||
| `cattle.clusterName` | The management cluster name |
|
||||
| `systemDefaultRegistry` | The default registry, taken from the settings |
|
||||
| `cattle.url` | The Rancher server URL, taken from the settings |
|
||||
| `cattle.rkePathPrefix` | The prefix path defined in the management cluster at `spec.rancherKubernetesEngineConfig.prefixPath` |
|
||||
| YAML Directive | Source |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| `cattle.clusterId` | The management cluster ID |
|
||||
| `cattle.clusterName` | The management cluster name |
|
||||
| `systemDefaultRegistry` | The default registry, taken from the settings |
|
||||
| `cattle.url` | The Rancher server URL, taken from the settings |
|
||||
| `cattle.rkePathPrefix` | The prefix path defined in the management cluster at `spec.rancherKubernetesEngineConfig.prefixPath` |
|
||||
| `cattle.rkeWindowsPathPrefix` | The Windows prefix path defined in the management cluster at `spec.rancherKubernetesEngineConfig.winPrefixPath` |
|
||||
| `cattle.windows` | Included in global values if `workerOSs` on the management cluster contains Windows |
|
||||
| `cattle.systemProjectId` | Taken from the ID of the project named System |
|
||||
| `cattle.windows` | Included in global values if `workerOSs` on the management cluster contains Windows |
|
||||
| `cattle.systemProjectId` | Taken from the ID of the project named System |
|
||||
|
||||
If there are two charts associated with an app, such as `rancher-monitoring` and `rancher-monitoring-crd`, Rancher injects the global values into the values of both charts.
|
||||
|
||||
|
|
@ -13,13 +13,13 @@ To tell Rancher about a new driver, go to Cluster Management -> Drivers -> Node
|
|||
|
||||
For more advanced control, the machine driver custom resource supports several annotations:
|
||||
|
||||
| Key | Value |
|
||||
| --------------------------|---------------------------------------------------------------------------------------------------|
|
||||
| publicCredentialFields | Fields that are considered "public" information and ok to display in cleartext for detail screens |
|
||||
| privateCredentialFields | Fields that are private information that should be stored as a secret |
|
||||
| optionalCredentialFields | Fields that are related to the credential, but are optional for the user to fill out |
|
||||
| passwordFields | Fields that should be displayed as type="password" bullets instead of cleartext |
|
||||
| defaults | Default values to set which the user may override |
|
||||
| Key | Value |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------------------- |
|
||||
| publicCredentialFields | Fields that are considered "public" information and ok to display in cleartext for detail screens |
|
||||
| privateCredentialFields | Fields that are private information that should be stored as a secret |
|
||||
| optionalCredentialFields | Fields that are related to the credential, but are optional for the user to fill out |
|
||||
| passwordFields | Fields that should be displayed as type="password" bullets instead of cleartext |
|
||||
| defaults | Default values to set which the user may override |
|
||||
|
||||
Each has a value that is a comma-separated list of field names. `defaults` are comma-separated, then colon-separated for key and value (e.g. `foo:bar,baz:42`). The annotations become information in API schemas, which the generic UI support uses to show better information.
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ Cloud Credentials store the username & password, or other similar information, n
|
|||
|
||||
The cloud credential component lives in the top-level `cloud-credential` directory in the repo. The file should be named the same as the driver, in all lowercase (e.g. `cloud-credential/digitalocean.vue`).
|
||||
|
||||
If there is a reason to rename it or map multiple drivers to the same credential, configure that in the [plugins store](`../../../../store/plugins.js`). There is also other info in there about how guesses are taken on what each field is for and how it should be displayed. These can be customized for your driver by importing and calling `configureCredential()` and `mapDriver()`.
|
||||
If there is a reason to rename it or map multiple drivers to the same credential, configure that in the [plugins store](../../shell/store/plugins.js). There is also other info in there about how guesses are taken on what each field is for and how it should be displayed. These can be customized for your driver by importing and calling `configureCredential()` and `mapDriver()`.
|
||||
|
||||
Create a component which displays each field that is relevant to authentication and lets the user configure them. Only the actual auth fields themselves, the rest of configuring the name, description, save buttons, etc is handled outside of the credential component.
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# Directory Structure
|
||||
|
||||
The directory structure is mostly flat, with each top level dir being for a different important thing (or just required by Nuxt to be there).
|
||||
|
||||
|
||||
## Other commonly changed stuff
|
||||
|
||||
| Path | Used for |
|
||||
| ---------- | ----------------------------------------------------------------------------------------------- |
|
||||
| assets | CSS, fonts, images, translations, etc resources which get processed during build |
|
||||
| components | All general components which don't have a separate special directory elsewhere |
|
||||
| layouts | The outermost components for rendering different kinds of pages (Nuxt) |
|
||||
| store | [Vuex](https://vuex.vuejs.org/) stores which maintain all the state for the life of a page load |
|
||||
| utils | Misc parsers, utilities, and other (usually) standalone code that doesn't fit anywhere else |
|
||||
|
||||
## The rest
|
||||
These are mostly standard Nuxt dirs that you won't need to go into very often.
|
||||
|
||||
| Path | Used for |
|
||||
| ---------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| static | Static files which get directly copied into the build with no processing |
|
||||
| middleware | Hooks called on every page load |
|
||||
| mixins | Code that is defined once and then applied to several different other components |
|
||||
| pages | The structure in here defines the routes that are available, and what gets rendered when one is hit |
|
||||
| plugins | Add-ons to modify vue/nuxt or load additional 3rd-party code. The "steve" API client also notably lives here. |
|
||||
| scripts | Shell scripts for building and related tasks, used by CI and `npm run ...` commands |
|
||||
| server | Server-side middleware and dev SSL cert |
|
||||
| test | Unit tests (or lack thereof) |
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Development Environment
|
||||
|
||||
This is part of the developer [getting started guide](../../../README.md).
|
||||
This is part of the developer [getting started guide](https://github.com/rancher/dashboard/blob/master/README.md).
|
||||
|
||||
## Stack
|
||||
|
||||
|
|
@ -8,17 +8,17 @@ A good base knowledge of Vue, Vuex and Nuxt should be reached before going throu
|
|||
|
||||
## Helpful links
|
||||
|
||||
Description | Link
|
||||
-----| ---
|
||||
Core Vue Docs | <https://vuejs.org/v2/guide>
|
||||
Typescript in Vue | <https://vuejs.org/v2/guide/typescript.html>
|
||||
Vue Template/Directive Shorthands | <https://vuejs.org/v2/guide/syntax.html>
|
||||
Vue Conditional rendering | <https://vuejs.org/v2/guide/conditional.html>
|
||||
Vuex Core Docs | <https://vuex.vuejs.org/>
|
||||
Nuxt Get Started | <https://nuxtjs.org/docs/2.x/get-started/installation>
|
||||
Nuxt Structure | <https://nuxtjs.org/docs/2.x/directory-structure>
|
||||
Axios (HTTP Requests) | <https://axios.nuxtjs.org/options>
|
||||
HTTP Proxy middleware | <https://github.com/nuxt-community/proxy-module> (<https://github.com/chimurai/http-proxy-middleware>)
|
||||
| Description | Link |
|
||||
| --------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| Core Vue Docs | <https://vuejs.org/v2/guide> |
|
||||
| Typescript in Vue | <https://vuejs.org/v2/guide/typescript.html> |
|
||||
| Vue Template/Directive Shorthands | <https://vuejs.org/v2/guide/syntax.html> |
|
||||
| Vue Conditional rendering | <https://vuejs.org/v2/guide/conditional.html> |
|
||||
| Vuex Core Docs | <https://vuex.vuejs.org/> |
|
||||
| Nuxt Get Started | <https://nuxtjs.org/docs/2.x/get-started/installation> |
|
||||
| Nuxt Structure | <https://nuxtjs.org/docs/2.x/directory-structure> |
|
||||
| Axios (HTTP Requests) | <https://axios.nuxtjs.org/options> |
|
||||
| HTTP Proxy middleware | <https://github.com/nuxt-community/proxy-module> (<https://github.com/chimurai/http-proxy-middleware>) |
|
||||
|
||||
## Platform
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ Developers are free to use the IDE and modern browser of their choosing. Here's
|
|||
|
||||
### Running the Dashboard
|
||||
|
||||
See the [Running For Development](../../../README.md#running-for-development) section on how to bring up the Dashboard locally
|
||||
See the [Running For Development](https://github.com/rancher/dashboard/blob/master/README.md#running-for-development) section on how to bring up the Dashboard locally
|
||||
|
||||
> Troubleshooting: Multiple `Could not freeze errors` in `yarn dev` terminal
|
||||
>
|
||||
|
Before Width: | Height: | Size: 245 KiB After Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 243 KiB |
|
Before Width: | Height: | Size: 444 KiB After Width: | Height: | Size: 444 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 209 KiB After Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 154 KiB |
|
Before Width: | Height: | Size: 350 KiB After Width: | Height: | Size: 350 KiB |
|
Before Width: | Height: | Size: 316 KiB After Width: | Height: | Size: 316 KiB |
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 312 KiB After Width: | Height: | Size: 312 KiB |
|
Before Width: | Height: | Size: 337 KiB After Width: | Height: | Size: 337 KiB |
|
Before Width: | Height: | Size: 248 KiB After Width: | Height: | Size: 248 KiB |
|
Before Width: | Height: | Size: 371 KiB After Width: | Height: | Size: 371 KiB |
|
Before Width: | Height: | Size: 195 KiB After Width: | Height: | Size: 195 KiB |
|
Before Width: | Height: | Size: 333 KiB After Width: | Height: | Size: 333 KiB |
|
Before Width: | Height: | Size: 575 KiB After Width: | Height: | Size: 575 KiB |
|
Before Width: | Height: | Size: 626 KiB After Width: | Height: | Size: 626 KiB |
|
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 298 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 400 KiB After Width: | Height: | Size: 400 KiB |
|
Before Width: | Height: | Size: 380 KiB After Width: | Height: | Size: 380 KiB |
|
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 207 KiB |
|
Before Width: | Height: | Size: 460 KiB After Width: | Height: | Size: 460 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 225 KiB |
|
|
@ -161,4 +161,4 @@ Node templates make it more convenient to define node pools so that you don't ha
|
|||
|
||||

|
||||
|
||||
For technical reasons that are better explained in the section on [cluster management resources,](../cluster-management-resources.md) node templates are not a planned feature for V2 cluster provisioning. For V2 provinioning, you have to fill in the machine configuration details when you create or edit the Kubernetes cluster through Rancher.
|
||||
For technical reasons that are better explained in the section on [cluster management resources,](../code-base-works/cluster-management-resources.md) node templates are not a planned feature for V2 cluster provisioning. For V2 provinioning, you have to fill in the machine configuration details when you create or edit the Kubernetes cluster through Rancher.
|
||||
|
|
@ -1,15 +1,19 @@
|
|||
### Customizing how Kubernetes Resources are Presented
|
||||
---
|
||||
sidebar_label: Customizing Kubernetes Resources
|
||||
---
|
||||
|
||||
# Customizing how Kubernetes Resources are Presented
|
||||
|
||||
These are where you do most of the daily work of customizing of how a particular k8s resource is presented.
|
||||
|
||||
Path | Used for
|
||||
-----|---------
|
||||
config | Configuration of how products look and work; constants for labels, types, cookies, query params, etc that are used
|
||||
chart | Custom components to present when installing a Product chart
|
||||
detail | Custom components to show as the detail view for a particular resource instance
|
||||
edit | Custom components to show as the edit (or view config) view for a particular resource instance
|
||||
list | Custom components to show as the list view for a resource type
|
||||
models | Custom logic extending the standard resource class for each API type and model returned by the API
|
||||
| Path | Used for |
|
||||
| ------ | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| config | Configuration of how products look and work; constants for labels, types, cookies, query params, etc that are used |
|
||||
| chart | Custom components to present when installing a Product chart |
|
||||
| detail | Custom components to show as the detail view for a particular resource instance |
|
||||
| edit | Custom components to show as the edit (or view config) view for a particular resource instance |
|
||||
| list | Custom components to show as the list view for a resource type |
|
||||
| models | Custom logic extending the standard resource class for each API type and model returned by the API |
|
||||
|
||||
There is one `Config` entry for each "product", which are the result of installing one of our helm charts to add a feature into Rancher such as Istio, monitoring, logging, CIS scans, etc. The config defines things like:
|
||||
- The condition for when that product should appear (usually the presence of a type in a certain k8s API group)
|
||||
|
|
@ -50,8 +50,8 @@ Now, back in the original console window, run:
|
|||
|
||||
```
|
||||
npm adduser --registry http://localhost:4873 (just add a user with username/password: admin/admin)
|
||||
yarn publish ./shell/creators/app --patch
|
||||
yarn publish ./shell/creators/pkg --patch
|
||||
yarn publish ./shell/creators/app --patch --registry http://localhost:4873/
|
||||
yarn publish ./shell/creators/pkg --patch --registry http://localhost:4873/
|
||||
```
|
||||
|
||||
These steps will publish the two [creator](https://classic.yarnpkg.com/en/docs/cli/create) packages to the local npm registry. If you open a browser to `http://127.0.0.1:4873` you'll see the two packages there.
|
||||
|
|
@ -59,12 +59,12 @@ These steps will publish the two [creator](https://classic.yarnpkg.com/en/docs/c
|
|||
Now we'll publish the `shell` package.
|
||||
|
||||
```
|
||||
yarn publish ./shell --patch
|
||||
PUBLISH_ARGS="--registry http://localhost:4873/" yarn publish-shell
|
||||
```
|
||||
|
||||
At this point, we've published 3 NPM packages:
|
||||
|
||||
- `@rancher/shell` - the core shell UI that can be incorporated into a new UI to give core funtionality
|
||||
- `@rancher/shell` - the core shell UI that can be incorporated into a new UI to give core functionality
|
||||
- `@rancher/create-app` - a simple package that can be used with the `yarn create` command to create a new UI
|
||||
- `@rancher/create-pkg` - a simple package that can be used with the `yarn create` command to create a new UI Package
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ yarn dev
|
|||
You should be able to open a browser at `https://127.0.0.1:8005` and you'll get the Rancher Dashboard UI.
|
||||
|
||||
This illustrates being able to share the `shell` across UIs - in practice, we would extract the Rancher code from the shell, so instead of getting the Rancher
|
||||
functionality in this example, you'd get an empty shell applciation with the side menu, UI chrome, avatar menu etc.
|
||||
functionality in this example, you'd get an empty shell application with the side menu, UI chrome, avatar menu etc.
|
||||
|
||||
You would now add new functionality to this application by adding new UI Packages.
|
||||
|
||||
|
|
@ -259,7 +259,7 @@ Now we can add our UI package that we published to the local registry with:
|
|||
yarn add testplugin
|
||||
```
|
||||
|
||||
Now when we run the app with `yarn dev` and browse to https://127.0.0.1:8005 you'll see that our `testplugin` plugin ahas been included in the UI.
|
||||
Now when we run the app with `yarn dev` and browse to https://127.0.0.1:8005 you'll see that our `testplugin` plugin has been included in the UI.
|
||||
|
||||
This supports the use case where we may be developing a UI Package that relies on another UI package - so we can create a skeleton app, add the dependencies we need via NPM and then develop our own plugin in the repository - so the only code we have is the code for out plugin, but we are able to test and run it in a UI locally with the other plugins that it needs.
|
||||
|
||||
|
|
@ -284,4 +284,4 @@ yarn link @rancher/shell
|
|||
|
||||
This link the package used by the app to the dashboard source code. We can make changes to the shell code in the Rancher Dashboard repository and the separate app will hot-reload.
|
||||
|
||||
This allows us to develop a new UI Application and be able to make changes to the Shell - in this use case, we're working against two git repositories, so we need to ensure we commit chanages accordingly.
|
||||
This allows us to develop a new UI Application and be able to make changes to the Shell - in this use case, we're working against two git repositories, so we need to ensure we commit changes accordingly.
|
||||
|
|
@ -64,7 +64,7 @@ Localisation files can be found in `./assets/translations/en-us.yaml`.
|
|||
|
||||
Please follow precedents in file to determine where new translations should be place.
|
||||
|
||||
Form fields are conventionally defined in translations as <some prefix>.<field name>.{label,description,enum options if applicable} e.g.
|
||||
Form fields are conventionally defined in translations as `<some prefix>`.`<field name>`.{label,description,enum options if applicable} e.g.
|
||||
|
||||
```yml
|
||||
account:
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
## Server-Side-Rendering (SSR)
|
||||
---
|
||||
sidebar_label: Server-side Rendering
|
||||
---
|
||||
|
||||
# Server-side-Rendering (SSR)
|
||||
|
||||
> Update: From Rancher 2.6.6 SSR mode is disabled for devs by default
|
||||
|
||||
|
|
@ -1,19 +1,9 @@
|
|||
# Style
|
||||
# Style and format
|
||||
|
||||
## SCSS
|
||||
|
||||
SCSS Styles can be found in `assets/styles/`. It's recommended to browse through some of the common styles in `_helpers.scss` and `_mixins.scss`.
|
||||
|
||||
### Icons
|
||||
Icons are font based and can be shown via the icon class
|
||||
|
||||
```
|
||||
<i class="icon icon-fw icon-gear" /></a>
|
||||
```
|
||||
|
||||
Icons can be browsed via `https://rancher.github.io/icons/`.
|
||||
|
||||
Additional icon styles can be found in via `assets/styles/fonts/_icons.scss`.
|
||||
|
||||
## Date
|
||||
The Dashboard uses the [dayjs](https://day.js.org/) library to handle dates, times and date algebra. However when showing a date and time they should take into account the date and time format. Therefore it's advised to use a formatter such as `/components/formatter/Date.vue` to display them.
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Terminology
|
||||
|
||||
This is part of the developer [getting started guide](https://github.com/rancher/dashboard/blob/master/README.md).
|
||||
|
||||
The official Kubernetes [glossary](https://kubernetes.io/docs/reference/glossary/?fundamental=true) also explains Kubernetes-specific terminology.
|
||||
|
||||
| Term | Description |
|
||||
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Dashboard / Cluster Explorer / Vue UI | The application in this repository. It will slowly replace the older Ember UI |
|
||||
| Manager / Cluster Manager / Ember UI | The old [Ember based UI](https://github.com/rancher/ui) |
|
||||
| Norman | Old Rancher API which has mostly been superseded by Steve |
|
||||
| Steve | New Rancher API |
|
||||
| Rancher (Product) | A [Kubernetes Management Platform](https://rancher.com/products/rancher/). This product includes the Rancher API and UIs |
|
||||
| RKE | [Rancher Kubernetes Engine](https://rancher.com/products/rke/) - A certified Kubernetes distribution |
|
||||
| SSR | Server Side Rendering. Disabled by default when developing the Dashboard (enabled pre 2.6.6) |
|
||||
| SPA | Single Page Application. Enabled by default in production |
|
||||
| Vue | [A frontend client framework used by the Dashboard](https://vuejs.org/) |
|
||||
| Vuex | [Frontend state management](https://vuex.vuejs.org/) |
|
||||
| Nuxt | [Vue framework helper](https://nuxtjs.org/) |
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
# E2E Test
|
||||
|
||||
This repo is configured for end-to-end testing with [Cypress](https://docs.cypress.io/api/table-of-contents) and the CI will run using a blank state of Rancher executed locally. The aim is however to enable also tests using remote instances of Ranchers.
|
||||
|
||||
Because of this, we extend the [Cypress best practices](https://docs.cypress.io/guides/references/best-practices#How-It-Works), so be sure to read them before write any test.
|
||||
|
||||
## Initial Setup
|
||||
|
||||
For the cypress test runner to consume the UI, you should specify the environment variables:
|
||||
|
||||
- Local authentication credentials
|
||||
- `TEST_USERNAME`, default `admin`
|
||||
- `TEST_PASSWORD`, user password or custom during first Rancher run
|
||||
- `CATTLE_BOOTSTRAP_PASSWORD`, initialization password which will also be used as `admin` password (do not pick `admin`)
|
||||
- `TEST_BASE_URL` // URL used by Cypress to run the tests, default `https://localhost:8005`
|
||||
- `TEST_SKIP_SETUP` // Avoid to execute bootstrap setup tests for already initialized Rancher instances
|
||||
- Dashboard
|
||||
- `TEST_PROJECT_ID` // Project ID used by Cypress/Sorry cypress to run the tests
|
||||
- `TEST_RUN_ID` (optional) // Identifier for your dashboard run, default value is timestamp
|
||||
|
||||
## Development with watch/dev
|
||||
|
||||
While writing the tests, you can simply run Rancher dashboard and then open the Cypress dashboard with the commands
|
||||
|
||||
- `yarn dev`
|
||||
- `yarn cy:open`
|
||||
|
||||
The Cypress dashboard will contain the options and the list of test suites. These will automatically re-run if they are altered (hot reloading).
|
||||
|
||||
For further information, consult [official documentation](https://docs.cypress.io/guides/guides/command-line#cypress-open).
|
||||
|
||||
## E2E Dashboard
|
||||
|
||||
### Self-hosted: Sorry Cypress
|
||||
|
||||
Link to the dashboard: http://139.59.134.103:8080/
|
||||
|
||||
E2E tests can be added and displayed in a dashboard by defining the project ID with the env var `TEST_PROJECT_ID`, then run the script:
|
||||
|
||||
```bash
|
||||
yarn cy:run:sorry
|
||||
```
|
||||
|
||||
### Cypress dashboard installation guide
|
||||
|
||||
The setup is done using a cloud hosting service and with its IP we configured the Sorry Cypress as indicated in the [guide](https://docs.sorry-cypress.dev/guide/dashboard-and-api). The process is straightforward, except for the IP which is required to be overwritten within `minio.yml` manifest as the default `http://localhost` value generate CORS issues.
|
||||
|
||||
### Cypress Dashboard
|
||||
|
||||
E2E tests can be displayed in Cypress dashboard by defining the project ID with the env var `TEST_PROJECT_ID`, then run the script by passing the parameters
|
||||
|
||||
```bash
|
||||
yarn cy:run --record --key YOUR_RECORD_KEY_HERE
|
||||
```
|
||||
|
||||
These values are provided when you create a new project within Cypress dashboard or within `Project settings`.
|
||||
|
||||
It's also possible to run a workflow in GitHub Actions E2E test using these values to record on personal dashboards.
|
||||
|
||||
## Local and CI/prod run
|
||||
|
||||
It is possible to start the project and run all the tests at once with a single command. There's however a difference between `dev` and `production` run. The first will not require an official certificate and will build the project in `.nuxt`, while the production will enable all the SSL configurations to run encrypted.
|
||||
|
||||
- `yarn e2e:pre-dev`, to optionally initialize Docker and build the project, if not already done
|
||||
- `yarn e2e:dev`, single run local development
|
||||
- `yarn e2e:pre-prod`, to optionally initialize Docker and build the project, required for GitHub Actions
|
||||
- `yarn e2e:dev`, for production use case and CI, which will also restart Docker and build the project
|
||||
|
||||
## Custom Commands
|
||||
|
||||
As Cypress common practice, some custom commands have been created within `command.ts` file to simplify the development process. Please consult Cypress documentation for more details about when and how to use them.
|
||||
|
||||
Worth mentioning the `cy.getId()` and `cy.findId()` commands, as it is mainly used to select elements. This would require to add `data-testid` to your element inside the markup and optionally matchers.
|
||||
|
||||
## Writing tests
|
||||
|
||||
Test specs should be grouped logically, normally by page or area of the Dashboard but also by a specific feature or component.
|
||||
|
||||
Tests should make use of common Page Object (PO) components. These can be pages or individual components which expose a useful set of tools, but most importantly contain the selectors for the DOM elements that need to be used. These will ensure changes to the underlying components don't require a rewrite of many many tests. They also allow parent components to easily search for children (for example easily finding all anchors in a section instead of the whole page). Given that tests are typescript it should be easy to explore the functionality.
|
||||
|
||||
Some examples of PO functionality
|
||||
|
||||
```ts
|
||||
HomePage.gotTo()
|
||||
new HomePagePo().checkIsCurrentPage()
|
||||
new BurgerMenuPo().clusters()
|
||||
new AsyncButtonPO('[data-testid="my-button"]').isDisabled()
|
||||
new LoginPagePo().username().set('admin')
|
||||
```
|
||||
|
||||
POs all inherit a root `component.po`. Common component functionality can be added there. They also expose their core cypress (chainable) element.
|
||||
|
||||
There are a large number of pages and components in the Dashboard and only a small set of POs. These will be expanded as the tests grow.
|
||||
|
||||
Note: When selecting an element be sure to use the attribute `data-testid`, even in case of lists where elements are distinguished by an index suffix.
|
||||
|
||||
## Tips
|
||||
|
||||
The Cypress UI is very much your friend. There you can click pick tests to run, easily visually track the progress of the test, see the before/after state of each cypress command (specifically good for debugging failed steps), see https requests, etc.
|
||||
|
||||
Tests can also be restricted before cypress runs, or at run time, by prepending `.only` to the run.
|
||||
|
||||
```ts
|
||||
describe.only('Burger Side Nav Menu', () => {
|
||||
beforeEach
|
||||
```
|
||||
|
||||
```ts
|
||||
it.only('Opens and closes on menu icon click', () => {
|
||||
```
|
||||
|
||||
## Data testid naming
|
||||
|
||||
While defining naming, always consider deterministic usage and rely on specific values. For cases where the content is required, e.g. select name specific elements as in cluster selection, consider use the `contain()` method. Further guideline and explanation in the official documentation related section.
|
||||
|
||||
In case of complex component, define a prefix for your `data-testid` with a the prop `componentTestid` and a default value. This will help you to define unique value and composable identifier in case of more elements, as well to avoid custom term for each test if not necessary, e.g. no multiple elements.
|
||||
|
||||
E.g. given the action menu:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* Inherited global identifier prefix for tests
|
||||
* Define a term based on the parent component to avoid conflicts on multiple components
|
||||
*/
|
||||
componentTestid: {
|
||||
type: String,
|
||||
default: 'action-menu'
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<li
|
||||
v-for="(option, i) in options"
|
||||
:key="opt.action"
|
||||
:data-testid="componentTestid + '-' + i + '-item'"
|
||||
>
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
To summarize what [defined in the documentation](https://docs.cypress.io/guides/guides/debugging), the following modalities of debugging are provided:
|
||||
|
||||
- `debugger` flag
|
||||
- `.debug()` as chained command
|
||||
- `cy.pause()` for analyzing the state of the test
|
||||
- Inspect commands in the Cypress dashboard to view the logs
|
||||
- `.then(console.log)` to append the log to the resolved promise
|
||||
|
||||
|
||||
These values are provided when you create a new project within Cypress dashboard or within `Project settings`.
|
||||
|
||||
## Coverage
|
||||
|
||||
Both unit and E2E tests generate coverage respectively with Jest and NYC. These values are generated on both PR and push to `master` and `release` after merging. The service used to display the values is Codecov and can be found [here](https://app.codecov.io/gh/rancher/dashboard).
|
||||
|
||||
Special attention goes to the E2E as the code is instrumented with Babel and the configuration is set within Nuxt.js.
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
# Unit test
|
||||
|
||||
The dashboard is configured to run unit tests with Jest in combination of vue-test-utils, for Vue scoped cases.
|
||||
|
||||
Requirements to accept tests:
|
||||
|
||||
- JS and TS formats
|
||||
- Suffix with `.test` or `.spec`
|
||||
- Contained in any directory `__tests__`
|
||||
|
||||
Adopted commands:
|
||||
|
||||
- `yarn test`, run and watch every test
|
||||
- `yarn test:ci`, script used for CI, which outputs a coverage report to `/coverage` folder
|
||||
|
||||
Example tests can be found in `/components/__tests__`. For more information about testing vue components, see the [vue test utils](https://vue-test-utils.vuejs.org/) and [jest](https://jestjs.io/docs/getting-started) docs.
|
||||
|
||||
## VSCode debugging tools
|
||||
|
||||
It is possible to use debugging tools within Jest via VSCode. To do so, open the debugger panel (Ctrl/Cmd+Shift+D) and select the `Debug Jest Tests` option from the dropdown. This will start a debug session with the Jest tests, allowing you to set breakpoint, inspect code and visualize variables on the panel itself. As usual it's possible to execute the tests by `F5` after selecting the right option.
|
||||
|
||||
## Style guide
|
||||
|
||||
On top of the recommendation provided by the [Vue documentation](https://vuejs.org/guide/scaling-up/testing.html), it is also encouraged to follow these patterns to create readable and aimed tests.
|
||||
|
||||
### Describe and test/it statement
|
||||
|
||||
To clearly state the scope of the test, it's convenient to define in the first `describe` always define with a noun the name of the function, method, or component being tested. Multiple assertions may be grouped together under a common statement in `describe` block, as it helps to avoid repetition and ensure a set of tests to be included. Each `test`/`it` block should then start with a verb related to what is the expectation.
|
||||
|
||||
```ts
|
||||
describe('myfunction', () => {
|
||||
describe('given the same parameter', () => {
|
||||
it('should return the same result', () => {
|
||||
// Test code
|
||||
});
|
||||
|
||||
it('should return something else for a second parameter', () => {
|
||||
// Test code
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
For further information, consult the [Jest API](https://jestjs.io/docs/api#describename-fn) documentation.
|
||||
|
||||
### Simple tests
|
||||
|
||||
Test with the highest readability and reliability should avoid logic, as this will increase line of code and have to be tested as well. Static data is then preferred over computation and should be declared always within the describe or test or as close as possible.
|
||||
|
||||
Don't:
|
||||
|
||||
```ts
|
||||
test('define if is required to use this in our component from the response', () => {
|
||||
const myData = externalFunction(externalData);
|
||||
|
||||
for(data in myData) {
|
||||
data.key = 'something else'
|
||||
}
|
||||
|
||||
expect(isRequired(myData)).toBe(true);
|
||||
});
|
||||
```
|
||||
|
||||
Do:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test('should return true', () => {
|
||||
const myData = { key: 'required case' };
|
||||
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### AAA pattern
|
||||
|
||||
Adoption of AAA format (arrange, act, assert) for tests.
|
||||
|
||||
- Arrange is where you prepare test, e.g. set properties to a component or declare variables
|
||||
- Act is when an event or function is triggered
|
||||
- Assertion correspond to the expectation of the test
|
||||
|
||||
Don't:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test('should return true', () => {
|
||||
let myData = { key: 'required case' };
|
||||
|
||||
expect(myData).toBeTruthy();
|
||||
|
||||
myData.key = 'something else';
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(true);
|
||||
|
||||
myData['key2'] = 'another key/value';
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Do:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test('should return true', () => {
|
||||
const myData = { key: 'required case' };
|
||||
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false if malformed data', () => {
|
||||
const myData = {
|
||||
key: 'required case',
|
||||
key2: 'another key/value'
|
||||
};
|
||||
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Behaviors over implementations
|
||||
|
||||
As also defined in the Vue documentation for [component](https://vuejs.org/guide/scaling-up/testing.html#component-testing) and [composable testing](https://vuejs.org/guide/scaling-up/testing.html#testing-composables), it is recommended to test rendered elements over internal API of the component.
|
||||
|
||||
Following an input example as in the [documentation](https://v2.vuejs.org/v2/cookbook/unit-testing-vue-components.html?redirect=true#Base-Example).
|
||||
|
||||
Don't:
|
||||
|
||||
```ts
|
||||
const wrapper = mount(YourComponent);
|
||||
const inputWrapper = wrapper.find(`[data-testid=your-component]`;
|
||||
|
||||
inputWrapper.setValue(1);
|
||||
|
||||
expect(wrapper.emitted('input')[0][0]).toBe(1);
|
||||
```
|
||||
|
||||
Do:
|
||||
|
||||
```ts
|
||||
const wrapper = mount(YourComponent);
|
||||
const inputWrapper = wrapper.find(`[data-testid=your-component]`;
|
||||
|
||||
inputWrapper.setValue(1);
|
||||
|
||||
expect(wrapper.text()).toContain('1')
|
||||
```
|
||||
|
||||
### Parameterization
|
||||
|
||||
When multiple cases are required to be tested for the same component, it is recommended to avoid multiple actions and assertions or even worse logic, but rather rely on Jest functions to parametrize the test.
|
||||
|
||||
Don't:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test('should return true', () => {
|
||||
let myData = { key: 'required case' };
|
||||
|
||||
expect(myData).toBeTruthy();
|
||||
|
||||
myData.key = 'something else';
|
||||
|
||||
expect(result).toBe(true);
|
||||
|
||||
myData.key = 'another value';
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Do:
|
||||
|
||||
```ts
|
||||
describe('FX: isRequired', () => {
|
||||
test.each([
|
||||
'required case',
|
||||
'something else',
|
||||
'another value',
|
||||
])('should return true', (key) => {
|
||||
const myData = { key };
|
||||
|
||||
const result = isRequired(myData);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Coverage
|
||||
|
||||
Both unit and E2E tests generate coverage respectively with Jest and NYC. These values are generated on both PR and push to `master` and `release` after merging. The service used to display the values is Codecov and can be found [here](https://app.codecov.io/gh/rancher/dashboard).
|
||||
|
||||
Special attention goes to the E2E as the code is instrumented with Babel and the configuration is set within Nuxt.js.
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// @ts-check
|
||||
// Note: type annotations allow type checking and IDEs autocompletion
|
||||
|
||||
const lightCodeTheme = require('prism-react-renderer/themes/github');
|
||||
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
title: 'Rancher UI DevKit',
|
||||
tagline: 'Rancher UI development kit',
|
||||
url: 'https://rancher.github.io',
|
||||
baseUrl: '/dashboard/',
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
favicon: 'img/favicon.ico',
|
||||
trailingSlash: false,
|
||||
|
||||
// GitHub pages deployment config.
|
||||
// If you aren't using GitHub pages, you don't need these.
|
||||
organizationName: 'rancher', // Usually your GitHub org/user name.
|
||||
projectName: 'dashboard', // Usually your repo name.
|
||||
|
||||
// Even if you don't use internalization, you can use this field to set useful
|
||||
// metadata like html lang. For example, if your site is Chinese, you may want
|
||||
// to replace "en" with "zh-Hans".
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en'],
|
||||
},
|
||||
|
||||
presets: [
|
||||
[
|
||||
'classic',
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: {
|
||||
routeBasePath: '/',
|
||||
sidebarPath: require.resolve('./sidebars.js'),
|
||||
showLastUpdateTime: true,
|
||||
// Please change this to your repo.
|
||||
// Remove this to remove the "edit this page" links.
|
||||
},
|
||||
blog: false,
|
||||
theme: { customCss: require.resolve('./src/css/custom.css') },
|
||||
}),
|
||||
],
|
||||
],
|
||||
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
navbar: {
|
||||
title: 'UI DevKit',
|
||||
logo: {
|
||||
alt: 'Rancher Logo',
|
||||
src: 'img/rancher-logo.svg',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'doc',
|
||||
docId: 'getting-started/concepts',
|
||||
position: 'right',
|
||||
label: 'Docs',
|
||||
},
|
||||
{
|
||||
href: 'https://rancher.github.io/storybook/',
|
||||
position: 'right',
|
||||
label: 'Components & Design kit',
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
style: 'dark',
|
||||
links: [
|
||||
{
|
||||
label: 'Stack',
|
||||
href: 'https://slack.rancher.io/',
|
||||
},
|
||||
{
|
||||
label: 'Github',
|
||||
href: 'https://github.com/rancher/',
|
||||
},
|
||||
],
|
||||
copyright: `Copyright © ${ new Date().getFullYear() } Rancher. All rights reserved.`,
|
||||
},
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "rancher-ui-devkit",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start",
|
||||
"build": "docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "2.0.0-beta.22",
|
||||
"@docusaurus/preset-classic": "2.0.0-beta.22",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.0",
|
||||
"prism-react-renderer": "^1.3.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.0.0-beta.22"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.5%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.14"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Creating a sidebar enables you to:
|
||||
- create an ordered group of docs
|
||||
- render a sidebar for each doc of that group
|
||||
- provide next/previous navigation
|
||||
|
||||
The sidebars can be generated from the filesystem, or explicitly defined here.
|
||||
|
||||
Create as many sidebars as you want.
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||
const sidebars = {
|
||||
// By default, Docusaurus generates a sidebar from the docs folder structure
|
||||
// tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }],
|
||||
|
||||
// But you can create a sidebar manually
|
||||
// Items name and page should be same.
|
||||
// For eg. if you rename a page you should also change that page name in item attribute under the tutorialSidebar.
|
||||
|
||||
tutorialSidebar: [
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Getting Started',
|
||||
items: ['getting-started/concepts', 'getting-started/development_environment', 'getting-started/ui-walkthrough'],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Guide',
|
||||
items: [
|
||||
'guide/build-for-container-registry',
|
||||
'guide/customising-how-k8s-resources-are-presented',
|
||||
'guide/forms-and-validation',
|
||||
'guide/package-management',
|
||||
'guide/plugins'
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'How code base works',
|
||||
items: [
|
||||
'code-base-works/api-resources-and-schemas',
|
||||
'code-base-works/auth-providers',
|
||||
'code-base-works/auth-sessions-and-tokens',
|
||||
'code-base-works/cluster-management-resources',
|
||||
'code-base-works/directory-structure',
|
||||
'code-base-works/helm-chart-apps',
|
||||
'code-base-works/keyboard-shortcuts',
|
||||
'code-base-works/machine-drivers',
|
||||
'code-base-works/performance',
|
||||
],
|
||||
},
|
||||
'on-screen-text-and-translations',
|
||||
'products-and-navigation',
|
||||
'server-side-rendering',
|
||||
'sortable-table',
|
||||
'style',
|
||||
'terminology',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Testing',
|
||||
items: ['testing/stress-test', 'testing/e2e-test', 'testing/unit-test'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = sidebars;
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const FeatureList = [
|
||||
{
|
||||
title: 'Getting Started',
|
||||
link: 'getting-started/concepts/',
|
||||
target: '',
|
||||
Svg: require('@site/static/img/documentation.svg').default,
|
||||
description: (
|
||||
<>
|
||||
This section covers the basics of working with the Rancher dashboard. It will familiarize you with the development
|
||||
environment, concepts, and new Rancher UI.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Components & Design kit',
|
||||
link: 'https://rancher.github.io/storybook/',
|
||||
target: '_blank',
|
||||
Svg: require('@site/static/img/storybook.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Rancher storybook is a collection of pre-built, reusable assets—components, patterns, and documentation guidance, to help
|
||||
developers to build consistent UI experiences faster.
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
function Feature( {
|
||||
Svg, title, description, link, target
|
||||
} ) {
|
||||
return (
|
||||
<div className={clsx('col col--6')}>
|
||||
<a className="featureLink" href={link} target={target}>
|
||||
<div className="text--center">
|
||||
<Svg className={styles.featureSvg} role="img" />
|
||||
</div>
|
||||
<div className="text--center padding-horiz--md">
|
||||
<h3>{title}</h3>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HomepageFeatures() {
|
||||
return (
|
||||
<section className={styles.features}>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{FeatureList.map((props, idx) => (
|
||||
<Feature key={idx} {...props} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
.features {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.featureSvg {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* Any CSS included here will be global. The classic template
|
||||
* bundles Infima by default. Infima is a CSS framework designed to
|
||||
* work well for content-centric websites.
|
||||
*/
|
||||
|
||||
/* You can override the default Infima variables here. */
|
||||
:root {
|
||||
--ifm-color-primary: #3D98D3;
|
||||
--ifm-color-primary-dark: #29784c;
|
||||
--ifm-color-primary-darker: #277148;
|
||||
--ifm-color-primary-darkest: #205d3b;
|
||||
--ifm-color-primary-light: #33925d;
|
||||
--ifm-color-primary-lighter: #359962;
|
||||
--ifm-color-primary-lightest: #3cad6e;
|
||||
--ifm-code-font-size: 95%;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
|
||||
--ifm-color-body: #141419;
|
||||
--ifm-menu-color: #141419;
|
||||
}
|
||||
|
||||
/* For readability concerns, you should choose a lighter palette in dark mode. */
|
||||
[data-theme='dark'] {
|
||||
--ifm-color-primary: #3D98D3;
|
||||
--ifm-color-primary-dark: #21af90;
|
||||
--ifm-color-primary-darker: #1fa588;
|
||||
--ifm-color-primary-darkest: #1a8870;
|
||||
--ifm-color-primary-light: #29d5b0;
|
||||
--ifm-color-primary-lighter: #32d8b4;
|
||||
--ifm-color-primary-lightest: #4fddbf;
|
||||
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
|
||||
--ifm-color-body: #ffffff;
|
||||
--ifm-menu-color: #ffffff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
body {
|
||||
font-family: 'Lato', sans-serif;
|
||||
color: var( --ifm-color-body);
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.navbar__logo {
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
|
||||
.homepage-banner {
|
||||
text-align: left;
|
||||
min-height: 300px;
|
||||
background: url(@site/static/img/footer-bg.png) no-repeat right bottom;
|
||||
background-size: 75%;
|
||||
padding: 50px;
|
||||
}
|
||||
|
||||
.homepage-banner p {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.featureSvg_src-components-HomepageFeatures-styles-module {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
|
||||
a.featureLink {
|
||||
color: var(--ifm-color-body);
|
||||
}
|
||||
|
||||
a.featureLink:hover {
|
||||
color: var(--ifm-link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 996px) {
|
||||
.homepage-banner {
|
||||
padding: 30px 30px;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.homepage-banner p {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.featureSvg_GfXr {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import Layout from '@theme/Layout';
|
||||
import HomepageFeatures from '@site/src/components/HomepageFeatures';
|
||||
|
||||
import styles from './index.module.css';
|
||||
|
||||
function HomepageHeader() {
|
||||
return (
|
||||
<header className={clsx('hero hero--primary', styles.heroBanner)}>
|
||||
<div className="container homepage-banner">
|
||||
<h1 className="hero__title">Rancher UI DevKit</h1>
|
||||
<p className="hero__subtitle">Rancher UI DevKit provides everything you need to start developing with the Rancher UI and plugins</p>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const { siteConfig } = useDocusaurusContext();
|
||||
|
||||
return (
|
||||
<Layout
|
||||
title={`${ siteConfig.title }`}
|
||||
description="Description will go into a meta tag in <head />">
|
||||
<HomepageHeader />
|
||||
<main>
|
||||
<HomepageFeatures />
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* CSS files with the .module.css suffix will be treated as CSS modules
|
||||
* and scoped locally.
|
||||
*/
|
||||
|
||||
.heroBanner {
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 996px) {
|
||||
.heroBanner {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><g id="b"><path id="c" d="M474.96357,305.2449l-78.23344-106.21121v-18.68469l7.87221-7.86811c19.02467-18.59241,19.37507-49.08707,.78266-68.11173-2.60402-2.66455-5.5084-5.01797-8.65487-7.01305v-30.22404c0-4.67655-3.79109-8.46765-8.46764-8.46766h-94.9429l-35.06972-47.61928c-2.75724-3.76885-8.04769-4.58891-11.81654-1.83166l-.02257,.01658-67.08422,49.41794H96.02975c-4.67656,0-8.46767,3.79108-8.46767,8.46764l.00002,.01644v59.13398L11.1191,182.56653c-3.76468,2.7711-4.57164,8.06868-1.80277,11.83501l78.24576,106.2071v131.91395c0,4.67655,3.79111,8.46764,8.46766,8.46764h94.93467l35.06974,47.60697c2.7667,3.76471,8.06146,4.57379,11.82619,1.80707l.01703-.01253,67.0719-49.40151h83.31322c4.67655,0,8.46764-3.79109,8.46764-8.46764v-59.13399l76.43477-56.30868c3.76362-2.77204,4.56743-8.07025,1.79538-11.83387l-.00085-.00113m-78.22931-149.82254v-34.78639c6.88643,10.57599,6.88643,24.21861,0,34.7946M249.63848,27.90653l22.64751,30.74146h-64.39035l41.74284-30.74146ZM27.98871,191.17792l59.57338-43.88645v124.75628L27.98871,191.17792ZM234.64965,471.73169l-22.64749-30.74146h64.39032l-41.74283,30.74146Zm-130.15225-47.68088V75.58741H379.79481v97.73533l-58.5796,58.5796c-7.06461,6.92536-18.40571,6.81248-25.33109-.25213-6.82888-6.96618-6.82797-18.11595,.00204-25.08103l56.2594-56.25941c1.94922-1.87529,5.03187-1.87529,6.98108,0,1.92779,1.9211,1.93321,5.04122,.01209,6.96899l-.01209,.01211-51.14676,51.13857c-3.27825,3.33516-3.2321,8.69639,.10305,11.97463,3.29463,3.23843,8.57695,3.23843,11.87159,0l51.14679-51.14269c8.54118-8.53179,8.54879-22.37216,.01701-30.91334l-.01701-.01701c-8.6529-8.271-22.28155-8.271-30.93445,0l-56.2594,56.25941c-13.78496,13.43071-14.07215,35.49336-.64146,49.27832,13.43071,13.78496,35.49336,14.07215,49.27832,.64144,.21659-.21103,.43045-.42485,.64146-.64144l46.60904-46.58853v226.7706H104.4974Zm292.23273-71.69584v-124.76448l59.57339,80.89857-59.57339,43.86591Z"/><path id="d" d="M137.64117,147.04508h113.42227c4.67768,0,8.46971-3.79202,8.46971-8.46971s-3.79203-8.46971-8.46971-8.46971h-113.42227c-4.67769,0-8.46971,3.79202-8.46971,8.46971s3.79202,8.46971,8.46971,8.46971"/><path id="e" d="M137.64117,221.20483h113.42227c4.67768,0,8.46971-3.79203,8.46971-8.46971s-3.79203-8.46971-8.46971-8.46971h-113.42227c-4.67769,0-8.46971,3.79203-8.46971,8.46971s3.79202,8.46971,8.46971,8.46971"/><path id="f" d="M346.28145,278.42929H137.63707c-4.67769,0-8.46971,3.79203-8.46971,8.46971s3.79202,8.46971,8.46971,8.46971h208.64439c4.67768,0,8.46971-3.79203,8.46971-8.46971s-3.79203-8.46971-8.46971-8.46971"/><path id="g" d="M346.28145,352.60136H137.63707c-4.67769,0-8.46973,3.79203-8.46973,8.46974s3.79203,8.46971,8.46973,8.46971h208.64439c4.67768,0,8.46971-3.79203,8.46971-8.46971s-3.79203-8.46971-8.46971-8.46971"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |