mirror of
https://github.com/cnrancher/kube-explorer.git
synced 2025-09-02 15:05:14 +00:00
Compare commits
43 Commits
v0.2.10-rc
...
v0.5.0-rc1
Author | SHA1 | Date | |
---|---|---|---|
|
896e03e279 | ||
|
979b4991fa | ||
|
aec9926ed8 | ||
|
2f3c1e6ab5 | ||
|
faa83722a0 | ||
|
cd955243b6 | ||
|
2b39db9f07 | ||
|
4dc1acb1f2 | ||
|
989d087b99 | ||
|
c214e6ba6a | ||
|
390b11caef | ||
|
e016261c4b | ||
|
c43288964a | ||
|
70e586976d | ||
|
d0ce0e28bf | ||
|
ad0a0c0cb3 | ||
|
651d499086 | ||
|
8e592b1a3c | ||
|
c1f5fda228 | ||
|
10e5323c95 | ||
|
ea49f9d3b4 | ||
|
b0b81ba87d | ||
|
e757347def | ||
|
f4970b85a2 | ||
|
bfae192748 | ||
|
3810cd702f | ||
|
f898c559e0 | ||
|
f0effa7f09 | ||
|
2838ceb34a | ||
|
40a972eeef | ||
|
88c924a816 | ||
|
d24282849f | ||
|
92aaca7407 | ||
|
c278dbb810 | ||
|
5c2ecdfb97 | ||
|
ecf6faba80 | ||
|
a89b9b46bf | ||
|
30c0ceef73 | ||
|
f6536c289e | ||
|
5347d02990 | ||
|
663e8d7682 | ||
|
dd8291f9e3 | ||
|
55e5418bd0 |
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/bin
|
||||||
|
/dist
|
||||||
|
/internal/ui/ui
|
294
.drone.yml
294
.drone.yml
@@ -1,294 +0,0 @@
|
|||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: default-amd64
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build
|
|
||||||
pull: default
|
|
||||||
image: rancher/dapper:v0.5.8
|
|
||||||
commands:
|
|
||||||
- dapper ci
|
|
||||||
privileged: true
|
|
||||||
volumes:
|
|
||||||
- name: docker
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
when:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
- name: release
|
|
||||||
pull: default
|
|
||||||
image: rancher/dapper:v0.5.8
|
|
||||||
commands:
|
|
||||||
- dapper ci
|
|
||||||
privileged: true
|
|
||||||
environment:
|
|
||||||
CROSS: 1
|
|
||||||
volumes:
|
|
||||||
- name: docker
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: stage-binaries-head
|
|
||||||
image: rancher/dapper:v0.5.8
|
|
||||||
commands:
|
|
||||||
- "cp -r ./bin/kube-explorer ./package/"
|
|
||||||
when:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: stage-binaries
|
|
||||||
image: rancher/dapper:v0.5.8
|
|
||||||
commands:
|
|
||||||
- "cp -r ./bin/kube-explorer-linux-amd64 ./package/kube-explorer"
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: github_binary_release
|
|
||||||
pull: default
|
|
||||||
image: plugins/github-release
|
|
||||||
settings:
|
|
||||||
api_key:
|
|
||||||
from_secret: github_token
|
|
||||||
checksum:
|
|
||||||
- sha256
|
|
||||||
files:
|
|
||||||
- "bin/*"
|
|
||||||
title: "${DRONE_TAG}"
|
|
||||||
overwrite: true
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: docker-publish-head
|
|
||||||
pull: default
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
dockerfile: package/Dockerfile
|
|
||||||
context: package/
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
repo: cnrancher/kube-explorer
|
|
||||||
tag: head-linux-amd64
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
when:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: docker-publish
|
|
||||||
pull: default
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
dockerfile: package/Dockerfile
|
|
||||||
context: package/
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
repo: cnrancher/kube-explorer
|
|
||||||
tag: ${DRONE_TAG}-linux-amd64
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: docker
|
|
||||||
host:
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
|
|
||||||
node:
|
|
||||||
instance: agent-amd64
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
- "refs/tags/*"
|
|
||||||
event:
|
|
||||||
exclude:
|
|
||||||
- promote
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: default-arm64
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: arm64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build
|
|
||||||
pull: default
|
|
||||||
image: rancher/dapper:v0.5.8
|
|
||||||
commands:
|
|
||||||
- dapper ci
|
|
||||||
privileged: true
|
|
||||||
volumes:
|
|
||||||
- name: docker
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
when:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
- "refs/tags/*"
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: stage-binaries
|
|
||||||
image: rancher/dapper:v0.5.8
|
|
||||||
commands:
|
|
||||||
- "cp -r ./bin/* ./package/"
|
|
||||||
when:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
- "refs/tags/*"
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
- tag
|
|
||||||
|
|
||||||
- name: docker-publish-head
|
|
||||||
pull: default
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
build_args:
|
|
||||||
- ARCH=arm64
|
|
||||||
dockerfile: package/Dockerfile
|
|
||||||
context: package/
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
repo: cnrancher/kube-explorer
|
|
||||||
tag: head-linux-arm64
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
when:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: docker-publish
|
|
||||||
pull: default
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
build_args:
|
|
||||||
- ARCH=arm64
|
|
||||||
dockerfile: package/Dockerfile
|
|
||||||
context: package/
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
repo: cnrancher/kube-explorer
|
|
||||||
tag: ${DRONE_TAG}-linux-arm64
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: docker
|
|
||||||
host:
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
- "refs/tags/*"
|
|
||||||
event:
|
|
||||||
exclude:
|
|
||||||
- promote
|
|
||||||
|
|
||||||
node:
|
|
||||||
instance: agent-arm64
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: manifest
|
|
||||||
|
|
||||||
platform:
|
|
||||||
os: linux
|
|
||||||
arch: amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: push-manifest-head
|
|
||||||
image: plugins/manifest
|
|
||||||
settings:
|
|
||||||
ignore_missing: true
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
spec: manifest-head.tmpl
|
|
||||||
when:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
event:
|
|
||||||
- push
|
|
||||||
|
|
||||||
- name: push-manifest
|
|
||||||
image: plugins/manifest
|
|
||||||
settings:
|
|
||||||
ignore_missing: true
|
|
||||||
username:
|
|
||||||
from_secret: docker_username
|
|
||||||
password:
|
|
||||||
from_secret: docker_password
|
|
||||||
spec: manifest.tmpl
|
|
||||||
when:
|
|
||||||
event:
|
|
||||||
- tag
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
- name: docker
|
|
||||||
host:
|
|
||||||
path: /var/run/docker.sock
|
|
||||||
|
|
||||||
node:
|
|
||||||
instance: agent-amd64
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
ref:
|
|
||||||
include:
|
|
||||||
- "refs/heads/main"
|
|
||||||
- "refs/heads/v*"
|
|
||||||
- "refs/tags/*"
|
|
||||||
event:
|
|
||||||
exclude:
|
|
||||||
- promote
|
|
||||||
|
|
||||||
depends_on:
|
|
||||||
- default-amd64
|
|
||||||
- default-arm64
|
|
||||||
|
|
||||||
...
|
|
165
.drone_backup.yml
Normal file
165
.drone_backup.yml
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
type: docker
|
||||||
|
kind: pipeline
|
||||||
|
name: push
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- promote
|
||||||
|
include:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
host:
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
|
||||||
|
node:
|
||||||
|
instance: agent-amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: rancher/dapper:v0.6.0
|
||||||
|
commands:
|
||||||
|
- dapper ci
|
||||||
|
environment:
|
||||||
|
CROSS: "${DRONE_BUILD_EVENT}"
|
||||||
|
privileged: true
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
|
||||||
|
- name: image-scan-head
|
||||||
|
image: aquasec/trivy
|
||||||
|
commands:
|
||||||
|
- trivy image --no-progress --ignore-unfixed --severity HIGH,CRITICAL --scanners vuln --exit-code 1 cnrancher/kube-explorer:${DRONE_COMMIT:0:7}
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
ref:
|
||||||
|
include:
|
||||||
|
- "refs/heads/main"
|
||||||
|
- "refs/heads/v*"
|
||||||
|
|
||||||
|
- name: install-buildx-support
|
||||||
|
image: tonistiigi/binfmt
|
||||||
|
privileged: true
|
||||||
|
entrypoint:
|
||||||
|
- /usr/bin/binfmt
|
||||||
|
command:
|
||||||
|
- --install
|
||||||
|
- all
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
ref:
|
||||||
|
include:
|
||||||
|
- "refs/heads/main"
|
||||||
|
- "refs/heads/v*"
|
||||||
|
|
||||||
|
- name: docker-publish
|
||||||
|
image: thegeeklab/drone-docker-buildx
|
||||||
|
privileged: true
|
||||||
|
settings:
|
||||||
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
dockerfile: package/Dockerfile
|
||||||
|
repo: cnrancher/kube-explorer
|
||||||
|
tag: latest
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
ref:
|
||||||
|
include:
|
||||||
|
- "refs/heads/main"
|
||||||
|
- "refs/heads/v*"
|
||||||
|
|
||||||
|
---
|
||||||
|
type: docker
|
||||||
|
kind: pipeline
|
||||||
|
name: tag
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- promote
|
||||||
|
include:
|
||||||
|
- tag
|
||||||
|
ref:
|
||||||
|
include:
|
||||||
|
- "refs/tags/*"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
host:
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
|
||||||
|
node:
|
||||||
|
instance: agent-amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: release
|
||||||
|
image: rancher/dapper:v0.6.0
|
||||||
|
commands:
|
||||||
|
- dapper ci
|
||||||
|
privileged: true
|
||||||
|
environment:
|
||||||
|
CROSS: "${DRONE_BUILD_EVENT}"
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
|
||||||
|
- name: install-buildx-support
|
||||||
|
image: tonistiigi/binfmt
|
||||||
|
privileged: true
|
||||||
|
entrypoint:
|
||||||
|
- /usr/bin/binfmt
|
||||||
|
command:
|
||||||
|
- --install
|
||||||
|
- all
|
||||||
|
|
||||||
|
- name: docker-publish
|
||||||
|
image: thegeeklab/drone-docker-buildx
|
||||||
|
privileged: true
|
||||||
|
settings:
|
||||||
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
dockerfile: package/Dockerfile
|
||||||
|
repo: cnrancher/kube-explorer
|
||||||
|
tag: ${DRONE_TAG}
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
volumes:
|
||||||
|
- name: docker
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
|
||||||
|
- name: github_binary_release
|
||||||
|
image: plugins/github-release
|
||||||
|
settings:
|
||||||
|
api_key:
|
||||||
|
from_secret: github_token
|
||||||
|
checksum:
|
||||||
|
- sha256
|
||||||
|
files:
|
||||||
|
- "bin/*"
|
||||||
|
title: "${DRONE_TAG}"
|
||||||
|
overwrite: true
|
21
.github/workflows/pr.yaml
vendored
Normal file
21
.github/workflows/pr.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: pull request
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
jobs:
|
||||||
|
pr-build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Commitsar check
|
||||||
|
uses: aevea/commitsar@v0.20.2
|
||||||
|
- name: Build to test
|
||||||
|
env:
|
||||||
|
SKIP_COMPRESS: "true"
|
||||||
|
run: make ci
|
81
.github/workflows/push.yaml
vendored
Normal file
81
.github/workflows/push.yaml
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
name: Push to Master
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- 'v*.*.*' # Matches any tag that starts with 'v' and follows semantic versioning
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Login to Aliyun ACR
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: registry.cn-shenzhen.aliyuncs.com
|
||||||
|
username: ${{ secrets.ACR_USERNAME }}
|
||||||
|
password: ${{ secrets.ACR_TOKEN }}
|
||||||
|
if: ${{ env.ALIYUN == 'true' }}
|
||||||
|
- name: Login to Dockerhub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: CI
|
||||||
|
if: startsWith(github.ref, 'refs/heads/')
|
||||||
|
env:
|
||||||
|
CROSS: push
|
||||||
|
run: make github_ci
|
||||||
|
|
||||||
|
- name: CI
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
env:
|
||||||
|
CROSS: tag
|
||||||
|
run: |
|
||||||
|
make github_ci
|
||||||
|
make release-note
|
||||||
|
|
||||||
|
- name: Prepare for packaging image
|
||||||
|
run: cp dist/* package/
|
||||||
|
- name: Set docker iamge name
|
||||||
|
id: image-name
|
||||||
|
env:
|
||||||
|
REPO_OVERRIDE: ${{ vars.REPO || 'cnrancher' }}
|
||||||
|
IMAGE_OVERRIDE: ${{ vars.IMAGE || 'kube-explorer' }}
|
||||||
|
run: |
|
||||||
|
tag_name=latest;
|
||||||
|
if [[ ${GITHUB_REF} == refs/tags/* ]]; then tag_name=${GITHUB_REF#refs/tags/}; fi;
|
||||||
|
echo "image_name=${REPO_OVERRIDE}/${IMAGE_OVERRIDE}:${tag_name}" >> $GITHUB_OUTPUT;
|
||||||
|
-
|
||||||
|
name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Build to Dockerhub
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64,linux/arm64/v8
|
||||||
|
tags: "${{ steps.image-name.outputs.image_name }}"
|
||||||
|
context: package
|
||||||
|
push: true
|
||||||
|
- name: Build to Aliyun
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
tags: registry.cn-shenzhen.aliyuncs.com/${{ steps.image-name.outputs.image_name }}
|
||||||
|
context: package
|
||||||
|
push: true
|
||||||
|
if: ${{ env.ALIYUN == 'true' }}
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
with:
|
||||||
|
files: dist/*
|
||||||
|
body_path: dist/release-note
|
||||||
|
draft: true
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -19,3 +19,7 @@
|
|||||||
/dist
|
/dist
|
||||||
/build
|
/build
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
/.vscode
|
||||||
|
/vendor
|
||||||
|
/internal/ui/ui/
|
||||||
|
68
.golangci.json
Normal file
68
.golangci.json
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"linters": {
|
||||||
|
"disable-all": true,
|
||||||
|
"enable": [
|
||||||
|
"govet",
|
||||||
|
"revive",
|
||||||
|
"goimports",
|
||||||
|
"misspell",
|
||||||
|
"ineffassign",
|
||||||
|
"gofmt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"linters-settings": {
|
||||||
|
"govet": {
|
||||||
|
"check-shadowing": false
|
||||||
|
},
|
||||||
|
"gofmt": {
|
||||||
|
"simplify": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"run": {
|
||||||
|
"skip-dirs": [
|
||||||
|
"vendor",
|
||||||
|
"tests",
|
||||||
|
"pkg/client",
|
||||||
|
"pkg/generated",
|
||||||
|
"scripts"
|
||||||
|
],
|
||||||
|
"tests": false,
|
||||||
|
"timeout": "10m"
|
||||||
|
},
|
||||||
|
"issues": {
|
||||||
|
"exclude-rules": [
|
||||||
|
{
|
||||||
|
"linters": "govet",
|
||||||
|
"text": "^(nilness|structtag)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path":"pkg/apis/management.cattle.io/v3/globaldns_types.go",
|
||||||
|
"text":".*lobalDns.*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pkg/apis/management.cattle.io/v3/zz_generated_register.go",
|
||||||
|
"text":".*lobalDns.*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path":"pkg/apis/management.cattle.io/v3/zz_generated_list_types.go",
|
||||||
|
"text":".*lobalDns.*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"linters": "revive",
|
||||||
|
"text": "should have comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"linters": "revive",
|
||||||
|
"text": "should be of the form"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"linters": "revive",
|
||||||
|
"text": "by other packages, and that stutters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"linters": "typecheck",
|
||||||
|
"text": "imported but not used as apierrors"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@@ -1,31 +1,28 @@
|
|||||||
FROM golang:1.17
|
FROM aevea/release-notary:0.9.2 as tools
|
||||||
|
|
||||||
|
FROM registry.suse.com/bci/golang:1.22
|
||||||
|
ARG PROXY
|
||||||
|
ARG GOPROXY
|
||||||
ARG DAPPER_HOST_ARCH
|
ARG DAPPER_HOST_ARCH
|
||||||
ENV HOST_ARCH=${DAPPER_HOST_ARCH} ARCH=${DAPPER_HOST_ARCH}
|
ENV HOST_ARCH=${DAPPER_HOST_ARCH} ARCH=${DAPPER_HOST_ARCH}
|
||||||
|
ENV https_proxy=${PROXY} \
|
||||||
|
http_proxy=${PROXY}
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN zypper -n install ca-certificates git-core wget curl unzip tar vim less file xz
|
||||||
apt-get install -y ca-certificates git wget curl xz-utils && \
|
RUN zypper install -y -f docker
|
||||||
rm -f /bin/sh && ln -s /bin/bash /bin/sh && \
|
|
||||||
curl -sL https://github.com/upx/upx/releases/download/v3.96/upx-3.96-${ARCH}_linux.tar.xz | tar xvJf - --strip-components=1 -C /tmp && \
|
ENV UPX_VERSION 4.2.1
|
||||||
|
RUN curl -sL https://github.com/upx/upx/releases/download/v${UPX_VERSION}/upx-${UPX_VERSION}-${ARCH}_linux.tar.xz | tar xvJf - --strip-components=1 -C /tmp && \
|
||||||
mv /tmp/upx /usr/bin/
|
mv /tmp/upx /usr/bin/
|
||||||
|
|
||||||
RUN if [ "${ARCH}" == "amd64" ]; then \
|
RUN if [ "${ARCH}" == "amd64" ]; then \
|
||||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.43.0; \
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.54.2; \
|
||||||
fi
|
fi
|
||||||
|
COPY --from=tools /app/release-notary /usr/local/bin/
|
||||||
ENV DOCKER_URL_amd64=https://get.docker.com/builds/Linux/x86_64/docker-1.10.3 \
|
ENV CATTLE_DASHBOARD_UI_VERSION="v2.8.0-kube-explorer-ui-rc3"
|
||||||
DOCKER_URL_arm=https://github.com/rancher/docker/releases/download/v1.10.3-ros1/docker-1.10.3_arm \
|
|
||||||
DOCKER_URL_arm64=https://github.com/rancher/docker/releases/download/v1.10.3-ros1/docker-1.10.3_arm64 \
|
ENV DAPPER_ENV REPO TAG DRONE_TAG CROSS GOPROXY SKIP_COMPRESS GITHUB_REPOSITORY GITHUB_TOKEN
|
||||||
DOCKER_URL=DOCKER_URL_${ARCH}
|
ENV DAPPER_SOURCE /go/src/github.com/cnrancher/kube-explorer
|
||||||
RUN wget -O - ${!DOCKER_URL} > /usr/bin/docker && chmod +x /usr/bin/docker
|
|
||||||
|
|
||||||
ENV GIT_COMMIT="d493afc3dbf4dd5f118fd069e5244caeaaecd6fb" \
|
|
||||||
GIT_BRANCH="ke/v0.2" \
|
|
||||||
GIT_SOURCE="/go/src/github.com/rancher/steve" \
|
|
||||||
CATTLE_DASHBOARD_UI_VERSION="v2.6.7-rc5-kube-explorer-ui-rc2"
|
|
||||||
|
|
||||||
ENV DAPPER_ENV REPO TAG DRONE_TAG CROSS
|
|
||||||
ENV DAPPER_SOURCE /opt/kube-explorer
|
|
||||||
ENV DAPPER_OUTPUT ./bin ./dist
|
ENV DAPPER_OUTPUT ./bin ./dist
|
||||||
ENV DAPPER_DOCKER_SOCKET true
|
ENV DAPPER_DOCKER_SOCKET true
|
||||||
ENV DAPPER_RUN_ARGS "-v ke-pkg:/go/pkg -v ke-cache:/root/.cache/go-build --privileged"
|
ENV DAPPER_RUN_ARGS "-v ke-pkg:/go/pkg -v ke-cache:/root/.cache/go-build --privileged"
|
||||||
|
12
deploy/kubectl/README.md
Normal file
12
deploy/kubectl/README.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
## Access Control Via Basic Auth
|
||||||
|
|
||||||
|
Deploy the kube-explorer workload:
|
||||||
|
|
||||||
|
```
|
||||||
|
kubectl create -f .
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure for different IngressClass:
|
||||||
|
|
||||||
|
- [Nginx Ingress](./nginx-auth)
|
||||||
|
- [Traefik Ingress](./traefik-v2-auth)
|
@@ -1,4 +1,4 @@
|
|||||||
## Traefik Auth
|
## Ingress-Nginx Basic Auth
|
||||||
|
|
||||||
This can be used in the cluster which uses the nginx-ingress.
|
This can be used in the cluster which uses the nginx-ingress.
|
||||||
|
|
||||||
@@ -13,9 +13,9 @@ htpasswd -nb username password | base64
|
|||||||
To install this mode, just run this script:
|
To install this mode, just run this script:
|
||||||
|
|
||||||
```
|
```
|
||||||
kubectl apply -f ./secret.yaml
|
kubectl create -f ./secret.yaml
|
||||||
export MY_XIP_IO=$(curl -sL ipinfo.io/ip)
|
export MY_IP=$(curl -sL ipinfo.io/ip)
|
||||||
envsubst < ./ingress.yaml.tpl | kubectl apply -f -
|
envsubst < ./ingress.yaml.tpl | kubectl create -f -
|
||||||
```
|
```
|
||||||
|
|
||||||
For more infos: https://kubernetes.github.io/ingress-nginx/examples/auth/basic/
|
For more infos: https://kubernetes.github.io/ingress-nginx/examples/auth/basic/
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
# Note: please replace the host first
|
# Note: please replace the host first
|
||||||
# To use xip.io: http://xip.io/
|
# To use sslip.io: https://sslip.io/
|
||||||
# To get your public IP: curl ipinfo.io/ip
|
# To get your public IP: curl ipinfo.io/ip
|
||||||
|
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: kube-explorer
|
name: kube-explorer
|
||||||
@@ -10,16 +10,18 @@ metadata:
|
|||||||
labels:
|
labels:
|
||||||
app: kube-explorer
|
app: kube-explorer
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: "nginx"
|
|
||||||
nginx.ingress.kubernetes.io/auth-type: basic
|
nginx.ingress.kubernetes.io/auth-type: basic
|
||||||
nginx.ingress.kubernetes.io/auth-secret: kube-explorer
|
nginx.ingress.kubernetes.io/auth-secret: kube-explorer
|
||||||
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - kube-explorer'
|
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - kube-explorer'
|
||||||
spec:
|
spec:
|
||||||
rules:
|
rules:
|
||||||
- host: "${MY_XIP_IO}.xip.io"
|
- host: "${MY_IP}.sslip.io"
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
serviceName: kube-explorer
|
service:
|
||||||
servicePort: 8989
|
name: kube-explorer
|
||||||
|
port:
|
||||||
|
number: 8989
|
||||||
|
@@ -13,9 +13,9 @@ htpasswd -nb username password | base64
|
|||||||
To install this mode, just run this script:
|
To install this mode, just run this script:
|
||||||
|
|
||||||
```
|
```
|
||||||
kubectl apply -f ./secret.yaml
|
kubectl create -f ./secret.yaml
|
||||||
export MY_XIP_IO=$(curl -sL ipinfo.io/ip)
|
export MY_IP=$(curl -sL ipinfo.io/ip)
|
||||||
envsubst < ./ingress.yaml.tpl | kubectl apply -f -
|
envsubst < ./ingress.yaml.tpl | kubectl create -f -
|
||||||
```
|
```
|
||||||
|
|
||||||
For more infos: https://doc.traefik.io/traefik/v1.7/configuration/backends/kubernetes/
|
For more infos: https://doc.traefik.io/traefik/v1.7/configuration/backends/kubernetes/
|
@@ -1,5 +1,5 @@
|
|||||||
# Note: please replace the host first
|
# Note: please replace the host first
|
||||||
# To use xip.io: http://xip.io/
|
# To use sslip.io: https://sslip.io/
|
||||||
# To get your public IP: curl ipinfo.io/ip
|
# To get your public IP: curl ipinfo.io/ip
|
||||||
|
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1beta1
|
||||||
@@ -16,7 +16,7 @@ metadata:
|
|||||||
ingress.kubernetes.io/auth-remove-header: "true"
|
ingress.kubernetes.io/auth-remove-header: "true"
|
||||||
spec:
|
spec:
|
||||||
rules:
|
rules:
|
||||||
- host: "${MY_XIP_IO}.xip.io"
|
- host: "${MY_IP}.sslip.io"
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
21
deploy/kubectl/traefik-v2-auth/README.md
Normal file
21
deploy/kubectl/traefik-v2-auth/README.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
## Traefik Auth
|
||||||
|
|
||||||
|
This can be used in K3s, as K3s use traefik as the default ingress class.
|
||||||
|
|
||||||
|
We use `basic-auth` to control the access of kube-explorer. The auth token is stored in the secret.
|
||||||
|
|
||||||
|
The default user is `niusmallnan`, and password is `dagedddd`. You can replace to another value with `htpasswd` tool.
|
||||||
|
|
||||||
|
```
|
||||||
|
htpasswd -nb username password | base64
|
||||||
|
```
|
||||||
|
|
||||||
|
To install this mode, just run this script:
|
||||||
|
|
||||||
|
```
|
||||||
|
kubectl create -f ./middleware.yaml
|
||||||
|
export MY_IP=$(curl -sL ipinfo.io/ip)
|
||||||
|
envsubst < ./ingress.yaml.tpl | kubectl create -f -
|
||||||
|
```
|
||||||
|
|
||||||
|
For more infos: https://doc.traefik.io/traefik/middlewares/http/basicauth/
|
25
deploy/kubectl/traefik-v2-auth/ingress.yaml.tpl
Normal file
25
deploy/kubectl/traefik-v2-auth/ingress.yaml.tpl
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Note: please replace the host first
|
||||||
|
# To use sslip.io.io: https://sslip.io.io/
|
||||||
|
# To get your public IP: curl ipinfo.io/ip
|
||||||
|
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: kube-explorer
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
app: kube-explorer
|
||||||
|
annotations:
|
||||||
|
traefik.ingress.kubernetes.io/router.middlewares: kube-system-kube-explorer@kubernetescrd
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: "${MY_IP}.sslip.io"
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: kube-explorer
|
||||||
|
port:
|
||||||
|
number: 8989
|
28
deploy/kubectl/traefik-v2-auth/middleware.yaml
Normal file
28
deploy/kubectl/traefik-v2-auth/middleware.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# The definitions below require the definitions for the Middleware and IngressRoute kinds.
|
||||||
|
# https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: kube-explorer
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
app: kube-explorer
|
||||||
|
spec:
|
||||||
|
basicAuth:
|
||||||
|
secret: kube-explorer
|
||||||
|
removeHeader: true
|
||||||
|
|
||||||
|
---
|
||||||
|
# To create an encoded user:password pair, the following command can be used:
|
||||||
|
# htpasswd -nb user password | base64
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: kube-explorer
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
app: kube-explorer
|
||||||
|
data:
|
||||||
|
auth: bml1c21hbGxuYW46JGFwcjEkbDdUZjJOdWskbmNXajYubHYvMGNkcXM0NFoyelVQLgoK
|
||||||
|
type: Opaque
|
117
go.mod
Normal file
117
go.mod
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
module github.com/cnrancher/kube-explorer
|
||||||
|
|
||||||
|
go 1.22.0
|
||||||
|
|
||||||
|
replace k8s.io/client-go => k8s.io/client-go v0.30.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gorilla/mux v1.8.1
|
||||||
|
github.com/rancher/apiserver v0.0.0-20240708202538-39a6f2535146
|
||||||
|
github.com/rancher/steve v0.0.0-20240709130809-47871606146c
|
||||||
|
github.com/rancher/wrangler/v3 v3.0.0
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
github.com/urfave/cli v1.22.15
|
||||||
|
golang.org/x/text v0.14.0
|
||||||
|
k8s.io/api v0.30.1
|
||||||
|
k8s.io/apimachinery v0.30.1
|
||||||
|
k8s.io/apiserver v0.30.1
|
||||||
|
k8s.io/client-go v12.0.0+incompatible
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/adrg/xdg v0.4.0 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||||
|
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||||
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
|
github.com/go-openapi/swag v0.22.3 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
|
github.com/google/gnostic-models v0.6.8 // indirect
|
||||||
|
github.com/google/go-cmp v0.6.0 // indirect
|
||||||
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.1 // indirect
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
|
github.com/imdario/mergo v0.3.12 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/pborman/uuid v1.2.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.16.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.4.0 // indirect
|
||||||
|
github.com/prometheus/common v0.44.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.10.1 // indirect
|
||||||
|
github.com/rancher/dynamiclistener v0.6.0-rc2 // indirect
|
||||||
|
github.com/rancher/kubernetes-provider-detector v0.1.5 // indirect
|
||||||
|
github.com/rancher/lasso v0.0.0-20240705194423-b2a060d103c1 // indirect
|
||||||
|
github.com/rancher/norman v0.0.0-20240708202514-a0127673d1b9 // indirect
|
||||||
|
github.com/rancher/remotedialer v0.3.2 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/urfave/cli/v2 v2.27.1 // indirect
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.19.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.19.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk v1.19.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.19.0 // indirect
|
||||||
|
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||||
|
golang.org/x/crypto v0.22.0 // indirect
|
||||||
|
golang.org/x/net v0.24.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.16.0 // indirect
|
||||||
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
|
golang.org/x/sys v0.19.0 // indirect
|
||||||
|
golang.org/x/term v0.19.0 // indirect
|
||||||
|
golang.org/x/time v0.3.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||||
|
google.golang.org/grpc v1.58.3 // indirect
|
||||||
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
k8s.io/apiextensions-apiserver v0.30.1 // indirect
|
||||||
|
k8s.io/component-base v0.30.1 // indirect
|
||||||
|
k8s.io/klog v1.0.0 // indirect
|
||||||
|
k8s.io/klog/v2 v2.120.1 // indirect
|
||||||
|
k8s.io/kube-aggregator v0.30.1 // indirect
|
||||||
|
k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 // indirect
|
||||||
|
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||||
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||||
|
modernc.org/libc v1.49.3 // indirect
|
||||||
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
|
modernc.org/memory v1.8.0 // indirect
|
||||||
|
modernc.org/sqlite v1.29.10 // indirect
|
||||||
|
modernc.org/strutil v1.2.0 // indirect
|
||||||
|
modernc.org/token v1.1.0 // indirect
|
||||||
|
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect
|
||||||
|
sigs.k8s.io/cli-utils v0.35.0 // indirect
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||||
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||||
|
)
|
28
internal/config/flags.go
Normal file
28
internal/config/flags.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var InsecureSkipTLSVerify bool
|
||||||
|
var SystemDefaultRegistry string
|
||||||
|
|
||||||
|
var ShellPodImage string
|
||||||
|
|
||||||
|
func Flags() []cli.Flag {
|
||||||
|
return []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "insecure-skip-tls-verify",
|
||||||
|
Destination: &InsecureSkipTLSVerify,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "system-default-registry",
|
||||||
|
Destination: &SystemDefaultRegistry,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "pod-image",
|
||||||
|
Destination: &ShellPodImage,
|
||||||
|
Value: "rancher/shell:v0.2.1-rc.7",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
80
internal/resources/cluster/cluster.go
Normal file
80
internal/resources/cluster/cluster.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rancher/apiserver/pkg/types"
|
||||||
|
"github.com/rancher/steve/pkg/podimpersonation"
|
||||||
|
"github.com/rancher/steve/pkg/resources/cluster"
|
||||||
|
"github.com/rancher/steve/pkg/server"
|
||||||
|
"golang.org/x/text/cases"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Register(_ context.Context, server *server.Server, displayName string) error {
|
||||||
|
cg := server.ClientFactory
|
||||||
|
shell := &shell{
|
||||||
|
cg: cg,
|
||||||
|
namespace: shellPodNS,
|
||||||
|
impersonator: podimpersonation.New("shell", cg, time.Hour, getShellPodImage),
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterSchema := server.BaseSchemas.LookupSchema("management.cattle.io.cluster")
|
||||||
|
if clusterSchema == nil {
|
||||||
|
return errors.New("failed to find management.cattle.io.cluster in base schema")
|
||||||
|
}
|
||||||
|
if clusterSchema.LinkHandlers == nil {
|
||||||
|
clusterSchema.LinkHandlers = make(map[string]http.Handler)
|
||||||
|
}
|
||||||
|
clusterSchema.LinkHandlers["shell"] = shell
|
||||||
|
clusterSchema.Store = func() types.Store {
|
||||||
|
return &displaynameWrapper{Store: clusterSchema.Store, displayName: displayName}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type displaynameWrapper struct {
|
||||||
|
types.Store
|
||||||
|
displayName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *displaynameWrapper) ByID(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
|
||||||
|
obj, err := s.Store.ByID(apiOp, schema, id)
|
||||||
|
if err != nil {
|
||||||
|
return obj, err
|
||||||
|
}
|
||||||
|
if obj.ID != "local" {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
if c, ok := obj.Object.(*cluster.Cluster); ok {
|
||||||
|
c.Spec.DisplayName = getDisplayNameWithContext(s.displayName)
|
||||||
|
}
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *displaynameWrapper) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) {
|
||||||
|
rtn, err := s.Store.List(apiOp, schema)
|
||||||
|
if err != nil {
|
||||||
|
return rtn, err
|
||||||
|
}
|
||||||
|
for _, obj := range rtn.Objects {
|
||||||
|
if obj.ID != "local" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c, ok := obj.Object.(*cluster.Cluster); ok {
|
||||||
|
c.Spec.DisplayName = getDisplayNameWithContext(s.displayName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rtn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDisplayNameWithContext(CurrentKubeContext string) string {
|
||||||
|
if CurrentKubeContext != "" {
|
||||||
|
return fmt.Sprintf("%s Cluster", cases.Title(language.English).String(CurrentKubeContext))
|
||||||
|
}
|
||||||
|
return "Local Cluster"
|
||||||
|
}
|
162
internal/resources/cluster/shell.go
Normal file
162
internal/resources/cluster/shell.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cnrancher/kube-explorer/internal/config"
|
||||||
|
"github.com/rancher/steve/pkg/podimpersonation"
|
||||||
|
"github.com/rancher/steve/pkg/stores/proxy"
|
||||||
|
"github.com/rancher/wrangler/v3/pkg/schemas/validation"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
"k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
shellPodNS = "kube-system"
|
||||||
|
)
|
||||||
|
|
||||||
|
type shell struct {
|
||||||
|
namespace string
|
||||||
|
impersonator *podimpersonation.PodImpersonation
|
||||||
|
cg proxy.ClientGetter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shell) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
ctx, user, client, err := s.contextAndClient(req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pod, err := s.impersonator.CreatePod(ctx, user, s.createPod(), &podimpersonation.PodOptions{
|
||||||
|
Wait: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||||
|
defer cancel()
|
||||||
|
client.CoreV1().Pods(pod.Namespace).Delete(ctx, pod.Name, metav1.DeleteOptions{})
|
||||||
|
s.impersonator.DeleteRole(ctx, *pod)
|
||||||
|
}()
|
||||||
|
s.proxyRequest(rw, req, pod, client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shell) proxyRequest(rw http.ResponseWriter, req *http.Request, pod *v1.Pod, client kubernetes.Interface) {
|
||||||
|
attachURL := client.CoreV1().RESTClient().
|
||||||
|
Get().
|
||||||
|
Namespace(pod.Namespace).
|
||||||
|
Resource("pods").
|
||||||
|
Name(pod.Name).
|
||||||
|
SubResource("exec").
|
||||||
|
VersionedParams(&v1.PodExecOptions{
|
||||||
|
Stdin: true,
|
||||||
|
Stdout: true,
|
||||||
|
Stderr: true,
|
||||||
|
TTY: true,
|
||||||
|
Container: "shell",
|
||||||
|
Command: []string{"welcome"},
|
||||||
|
}, scheme.ParameterCodec).URL()
|
||||||
|
|
||||||
|
httpClient := client.CoreV1().RESTClient().(*rest.RESTClient).Client
|
||||||
|
p := httputil.ReverseProxy{
|
||||||
|
Director: func(req *http.Request) {
|
||||||
|
req.URL = attachURL
|
||||||
|
req.Host = attachURL.Host
|
||||||
|
delete(req.Header, "Impersonate-Group")
|
||||||
|
delete(req.Header, "Impersonate-User")
|
||||||
|
delete(req.Header, "Authorization")
|
||||||
|
delete(req.Header, "Cookie")
|
||||||
|
},
|
||||||
|
Transport: httpClient.Transport,
|
||||||
|
FlushInterval: time.Millisecond * 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
p.ServeHTTP(rw, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shell) contextAndClient(req *http.Request) (context.Context, user.Info, kubernetes.Interface, error) {
|
||||||
|
ctx := req.Context()
|
||||||
|
client, err := s.cg.AdminK8sInterface()
|
||||||
|
if err != nil {
|
||||||
|
return ctx, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, ok := request.UserFrom(ctx)
|
||||||
|
if !ok {
|
||||||
|
return ctx, nil, nil, validation.Unauthorized
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx, user, client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shell) createPod() *v1.Pod {
|
||||||
|
return &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
GenerateName: "dashboard-shell-",
|
||||||
|
Namespace: s.namespace,
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
TerminationGracePeriodSeconds: new(int64),
|
||||||
|
RestartPolicy: v1.RestartPolicyNever,
|
||||||
|
NodeSelector: map[string]string{
|
||||||
|
"kubernetes.io/os": "linux",
|
||||||
|
},
|
||||||
|
Tolerations: []v1.Toleration{
|
||||||
|
{
|
||||||
|
Key: "cattle.io/os",
|
||||||
|
Operator: "Equal",
|
||||||
|
Value: "linux",
|
||||||
|
Effect: "NoSchedule",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "node-role.kubernetes.io/controlplane",
|
||||||
|
Operator: "Equal",
|
||||||
|
Value: "true",
|
||||||
|
Effect: "NoSchedule",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "node-role.kubernetes.io/etcd",
|
||||||
|
Operator: "Equal",
|
||||||
|
Value: "true",
|
||||||
|
Effect: "NoExecute",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "shell",
|
||||||
|
TTY: true,
|
||||||
|
Stdin: true,
|
||||||
|
StdinOnce: true,
|
||||||
|
Env: []v1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: "KUBECONFIG",
|
||||||
|
Value: "/home/shell/.kube/config",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Image: getShellPodImage(),
|
||||||
|
ImagePullPolicy: v1.PullIfNotPresent,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getShellPodImage() string {
|
||||||
|
if config.SystemDefaultRegistry == "" {
|
||||||
|
return config.ShellPodImage
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s/%s", config.SystemDefaultRegistry, config.ShellPodImage)
|
||||||
|
}
|
93
internal/server/config.go
Normal file
93
internal/server/config.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rancher/apiserver/pkg/types"
|
||||||
|
steveauth "github.com/rancher/steve/pkg/auth"
|
||||||
|
"github.com/rancher/steve/pkg/schema"
|
||||||
|
"github.com/rancher/steve/pkg/server"
|
||||||
|
"github.com/rancher/steve/pkg/server/cli"
|
||||||
|
"github.com/rancher/steve/pkg/server/router"
|
||||||
|
"github.com/rancher/wrangler/v3/pkg/kubeconfig"
|
||||||
|
"github.com/rancher/wrangler/v3/pkg/ratelimit"
|
||||||
|
|
||||||
|
"github.com/cnrancher/kube-explorer/internal/config"
|
||||||
|
"github.com/cnrancher/kube-explorer/internal/resources/cluster"
|
||||||
|
"github.com/cnrancher/kube-explorer/internal/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToServer(ctx context.Context, c *cli.Config, sqlCache bool) (*server.Server, error) {
|
||||||
|
var (
|
||||||
|
auth steveauth.Middleware
|
||||||
|
)
|
||||||
|
|
||||||
|
restConfig, err := kubeconfig.GetNonInteractiveClientConfigWithContext(c.KubeConfig, c.Context).ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
restConfig.RateLimiter = ratelimit.None
|
||||||
|
|
||||||
|
restConfig.Insecure = config.InsecureSkipTLSVerify
|
||||||
|
if restConfig.Insecure {
|
||||||
|
restConfig.CAData = nil
|
||||||
|
restConfig.CAFile = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.WebhookConfig.WebhookAuthentication {
|
||||||
|
auth, err = c.WebhookConfig.WebhookMiddleware()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
controllers, err := server.NewController(restConfig, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
steveServer, err := server.New(ctx, restConfig, &server.Options{
|
||||||
|
AuthMiddleware: auth,
|
||||||
|
Controllers: controllers,
|
||||||
|
Next: ui.New(c.UIPath),
|
||||||
|
SQLCache: sqlCache,
|
||||||
|
// router needs to hack here
|
||||||
|
Router: func(h router.Handlers) http.Handler {
|
||||||
|
return rewriteLocalCluster(router.Routes(h))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// registrer local cluster
|
||||||
|
if err := cluster.Register(ctx, steveServer, c.Context); err != nil {
|
||||||
|
return steveServer, err
|
||||||
|
}
|
||||||
|
// wrap default store
|
||||||
|
steveServer.SchemaFactory.AddTemplate(schema.Template{
|
||||||
|
Customize: func(a *types.APISchema) {
|
||||||
|
if a.Store == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a.Store = &deleteOptionStore{
|
||||||
|
Store: a.Store,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return steveServer, controllers.Start(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteLocalCluster(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if strings.HasPrefix(req.URL.Path, "/k8s/clusters/local") {
|
||||||
|
req.URL.Path = strings.TrimPrefix(req.URL.Path, "/k8s/clusters/local")
|
||||||
|
if req.URL.Path == "" {
|
||||||
|
req.URL.Path = "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next.ServeHTTP(rw, req)
|
||||||
|
})
|
||||||
|
}
|
16
internal/server/delete_option_store.go
Normal file
16
internal/server/delete_option_store.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rancher/apiserver/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deleteOptionStore struct {
|
||||||
|
types.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *deleteOptionStore) Delete(apiOp *types.APIRequest, schema *types.APISchema, id string) (types.APIObject, error) {
|
||||||
|
query := apiOp.Request.URL.Query()
|
||||||
|
query.Add("propagationPolicy", "Background")
|
||||||
|
apiOp.Request.URL.RawQuery = query.Encode()
|
||||||
|
return s.Store.Delete(apiOp, schema, id)
|
||||||
|
}
|
91
internal/ui/embed.go
Normal file
91
internal/ui/embed.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
//go:build embed
|
||||||
|
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// content holds our static web server content.
|
||||||
|
//
|
||||||
|
//go:embed all:ui/*
|
||||||
|
var staticContent embed.FS
|
||||||
|
|
||||||
|
type fsFunc func(name string) (fs.File, error)
|
||||||
|
|
||||||
|
func (f fsFunc) Open(name string) (fs.File, error) {
|
||||||
|
return f(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathExist(path string) bool {
|
||||||
|
_, err := staticContent.Open(path)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func openFile(path string) (fs.File, error) {
|
||||||
|
file, err := staticContent.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("openEmbedFile %s err: %v", path, err)
|
||||||
|
}
|
||||||
|
return file, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveEmbed(basePaths ...string) http.Handler {
|
||||||
|
handler := fsFunc(func(name string) (fs.File, error) {
|
||||||
|
logrus.Debugf("serveEmbed name: %s", name)
|
||||||
|
assetPath := joinEmbedFilepath(append(basePaths, name)...)
|
||||||
|
logrus.Debugf("serveEmbed final path: %s", assetPath)
|
||||||
|
return openFile(assetPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
return http.FileServer(http.FS(handler))
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveEmbedIndex(basePath string) http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
path := joinEmbedFilepath(basePath, "dashboard", "index.html")
|
||||||
|
logrus.Debugf("serveEmbedIndex : %s", path)
|
||||||
|
f, _ := staticContent.Open(path)
|
||||||
|
io.Copy(rw, f)
|
||||||
|
f.Close()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) ServeAsset() http.Handler {
|
||||||
|
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
serveEmbed(u.pathSetting()).ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) ServeFaviconDashboard() http.Handler {
|
||||||
|
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
serveEmbed(u.pathSetting(), "dashboard").ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) IndexFileOnNotFound() http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
path := joinEmbedFilepath(u.pathSetting(), req.URL.Path)
|
||||||
|
if pathExist(path) {
|
||||||
|
u.ServeAsset().ServeHTTP(rw, req)
|
||||||
|
} else {
|
||||||
|
u.IndexFile().ServeHTTP(rw, req)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) IndexFile() http.Handler {
|
||||||
|
return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
serveEmbedIndex(u.pathSetting()).ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinEmbedFilepath(paths ...string) string {
|
||||||
|
return filepath.ToSlash(filepath.Join(paths...))
|
||||||
|
}
|
42
internal/ui/external.go
Normal file
42
internal/ui/external.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//go:build !embed
|
||||||
|
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (u *Handler) ServeAsset() http.Handler {
|
||||||
|
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
http.FileServer(http.Dir(u.pathSetting())).ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) ServeFaviconDashboard() http.Handler {
|
||||||
|
return u.middleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
http.FileServer(http.Dir(filepath.Join(u.pathSetting(), "dashboard"))).ServeHTTP(rw, req)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) IndexFileOnNotFound() http.Handler {
|
||||||
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
// we ignore directories here because we want those to come from the CDN when running in that mode
|
||||||
|
if stat, err := os.Stat(filepath.Join(u.pathSetting(), req.URL.Path)); err == nil && !stat.IsDir() {
|
||||||
|
u.ServeAsset().ServeHTTP(rw, req)
|
||||||
|
} else {
|
||||||
|
u.IndexFile().ServeHTTP(rw, req)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) IndexFile() http.Handler {
|
||||||
|
return u.indexMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if path, isURL := u.path(); isURL {
|
||||||
|
_ = serveIndex(rw, path)
|
||||||
|
} else {
|
||||||
|
http.ServeFile(rw, req, filepath.Join(path, "index.html"))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
141
internal/ui/handler.go
Normal file
141
internal/ui/handler.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/rancher/apiserver/pkg/middleware"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultPath = "./ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
insecureClient = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type StringSetting func() string
|
||||||
|
type BoolSetting func() bool
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
pathSetting func() string
|
||||||
|
indexSetting func() string
|
||||||
|
releaseSetting func() bool
|
||||||
|
offlineSetting func() string
|
||||||
|
middleware func(http.Handler) http.Handler
|
||||||
|
indexMiddleware func(http.Handler) http.Handler
|
||||||
|
|
||||||
|
downloadOnce sync.Once
|
||||||
|
downloadSuccess bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
// The location on disk of the UI files
|
||||||
|
Path StringSetting
|
||||||
|
// The HTTP URL of the index file to download
|
||||||
|
Index StringSetting
|
||||||
|
// Whether or not to run the UI offline, should return true/false/dynamic
|
||||||
|
Offline StringSetting
|
||||||
|
// Whether or not is it release, if true UI will run offline if set to dynamic
|
||||||
|
ReleaseSetting BoolSetting
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUIHandler(opts *Options) *Handler {
|
||||||
|
if opts == nil {
|
||||||
|
opts = &Options{}
|
||||||
|
}
|
||||||
|
|
||||||
|
h := &Handler{
|
||||||
|
indexSetting: opts.Index,
|
||||||
|
offlineSetting: opts.Offline,
|
||||||
|
pathSetting: opts.Path,
|
||||||
|
releaseSetting: opts.ReleaseSetting,
|
||||||
|
middleware: middleware.Chain{
|
||||||
|
middleware.Gzip,
|
||||||
|
middleware.FrameOptions,
|
||||||
|
middleware.CacheMiddleware("json", "js", "css"),
|
||||||
|
}.Handler,
|
||||||
|
indexMiddleware: middleware.Chain{
|
||||||
|
middleware.Gzip,
|
||||||
|
middleware.NoCache,
|
||||||
|
middleware.FrameOptions,
|
||||||
|
middleware.ContentType,
|
||||||
|
}.Handler,
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.indexSetting == nil {
|
||||||
|
h.indexSetting = func() string {
|
||||||
|
return "https://releases.rancher.com/dashboard/latest/index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.offlineSetting == nil {
|
||||||
|
h.offlineSetting = func() string {
|
||||||
|
return "dynamic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.pathSetting == nil {
|
||||||
|
h.pathSetting = func() string {
|
||||||
|
return defaultPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.releaseSetting == nil {
|
||||||
|
h.releaseSetting = func() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) path() (path string, isURL bool) {
|
||||||
|
switch u.offlineSetting() {
|
||||||
|
case "dynamic":
|
||||||
|
if u.releaseSetting() {
|
||||||
|
return u.pathSetting(), false
|
||||||
|
}
|
||||||
|
if u.canDownload(u.indexSetting()) {
|
||||||
|
return u.indexSetting(), true
|
||||||
|
}
|
||||||
|
return u.pathSetting(), false
|
||||||
|
case "true":
|
||||||
|
return u.pathSetting(), false
|
||||||
|
default:
|
||||||
|
return u.indexSetting(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Handler) canDownload(url string) bool {
|
||||||
|
u.downloadOnce.Do(func() {
|
||||||
|
if err := serveIndex(io.Discard, url); err == nil {
|
||||||
|
u.downloadSuccess = true
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("Failed to download %s, falling back to packaged UI", url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return u.downloadSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveIndex(resp io.Writer, url string) error {
|
||||||
|
r, err := insecureClient.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Body.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(resp, r.Body)
|
||||||
|
return err
|
||||||
|
}
|
48
internal/ui/routers.go
Normal file
48
internal/ui/routers.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cnrancher/kube-explorer/internal/version"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(path string) http.Handler {
|
||||||
|
vue := NewUIHandler(&Options{
|
||||||
|
Path: func() string {
|
||||||
|
if path == "" {
|
||||||
|
return defaultPath
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
},
|
||||||
|
Offline: func() string {
|
||||||
|
if path != "" {
|
||||||
|
return "true"
|
||||||
|
}
|
||||||
|
return "dynamic"
|
||||||
|
},
|
||||||
|
ReleaseSetting: func() bool {
|
||||||
|
return version.IsRelease()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
router := mux.NewRouter()
|
||||||
|
router.UseEncodedPath()
|
||||||
|
|
||||||
|
router.Handle("/", http.RedirectHandler("/dashboard/", http.StatusFound))
|
||||||
|
router.Handle("/dashboard", http.RedirectHandler("/dashboard/", http.StatusFound))
|
||||||
|
router.Handle("/dashboard/", vue.IndexFile())
|
||||||
|
router.Handle("/favicon.png", vue.ServeFaviconDashboard())
|
||||||
|
router.Handle("/favicon.ico", vue.ServeFaviconDashboard())
|
||||||
|
router.PathPrefix("/dashboard/").Handler(vue.IndexFileOnNotFound())
|
||||||
|
router.PathPrefix("/k8s/clusters/local").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
url := strings.TrimPrefix(req.URL.Path, "/k8s/clusters/local")
|
||||||
|
if url == "" {
|
||||||
|
url = "/"
|
||||||
|
}
|
||||||
|
http.Redirect(rw, req, url, http.StatusFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
23
internal/version/version.go
Normal file
23
internal/version/version.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Version = "dev"
|
||||||
|
GitCommit = "HEAD"
|
||||||
|
|
||||||
|
// K-EXPLORER
|
||||||
|
releasePattern = regexp.MustCompile("^v[0-9]")
|
||||||
|
)
|
||||||
|
|
||||||
|
func FriendlyVersion() string {
|
||||||
|
return fmt.Sprintf("%s (%s)", Version, GitCommit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsRelease() bool {
|
||||||
|
return !strings.Contains(Version, "dev") && releasePattern.MatchString(Version)
|
||||||
|
}
|
55
main.go
Normal file
55
main.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/rancher/steve/pkg/debug"
|
||||||
|
stevecli "github.com/rancher/steve/pkg/server/cli"
|
||||||
|
"github.com/rancher/steve/pkg/version"
|
||||||
|
"github.com/rancher/wrangler/v3/pkg/signals"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
|
keconfig "github.com/cnrancher/kube-explorer/internal/config"
|
||||||
|
"github.com/cnrancher/kube-explorer/internal/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
config stevecli.Config
|
||||||
|
debugconfig debug.Config
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Name = "kube-explorer"
|
||||||
|
app.Version = version.FriendlyVersion()
|
||||||
|
app.Usage = ""
|
||||||
|
app.Flags = joinFlags(
|
||||||
|
stevecli.Flags(&config),
|
||||||
|
debug.Flags(&debugconfig),
|
||||||
|
keconfig.Flags(),
|
||||||
|
)
|
||||||
|
app.Action = run
|
||||||
|
|
||||||
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(_ *cli.Context) error {
|
||||||
|
ctx := signals.SetupSignalContext()
|
||||||
|
debugconfig.MustSetupDebug()
|
||||||
|
s, err := server.ToServer(ctx, &config, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.ListenAndServe(ctx, config.HTTPSListenPort, config.HTTPListenPort, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinFlags(flags ...[]cli.Flag) []cli.Flag {
|
||||||
|
var rtn []cli.Flag
|
||||||
|
for _, flag := range flags {
|
||||||
|
rtn = append(rtn, flag...)
|
||||||
|
}
|
||||||
|
return rtn
|
||||||
|
}
|
@@ -1,12 +0,0 @@
|
|||||||
image: cnrancher/kube-explorer:latest
|
|
||||||
manifests:
|
|
||||||
-
|
|
||||||
image: cnrancher/kube-explorer:head-linux-amd64
|
|
||||||
platform:
|
|
||||||
architecture: amd64
|
|
||||||
os: linux
|
|
||||||
-
|
|
||||||
image: cnrancher/kube-explorer:head-linux-arm64
|
|
||||||
platform:
|
|
||||||
architecture: arm64
|
|
||||||
os: linux
|
|
@@ -1,12 +0,0 @@
|
|||||||
image: cnrancher/kube-explorer:{{build.tag}}
|
|
||||||
manifests:
|
|
||||||
-
|
|
||||||
image: cnrancher/kube-explorer:{{build.tag}}-linux-amd64
|
|
||||||
platform:
|
|
||||||
architecture: amd64
|
|
||||||
os: linux
|
|
||||||
-
|
|
||||||
image: cnrancher/kube-explorer:{{build.tag}}-linux-arm64
|
|
||||||
platform:
|
|
||||||
architecture: arm64
|
|
||||||
os: linux
|
|
@@ -1,6 +1,7 @@
|
|||||||
FROM alpine:3.13
|
FROM registry.suse.com/bci/bci-minimal:15.6
|
||||||
|
ARG TARGETARCH
|
||||||
COPY kube-explorer entrypoint.sh /usr/bin/
|
ARG TARGETOS
|
||||||
# Hack to make golang do files,dns search order
|
ENV ARCH=${TARGETARCH:-"amd64"} OS=${TARGETOS:-"linux"}
|
||||||
ENV LOCALDOMAIN=""
|
COPY entrypoint.sh /usr/bin/
|
||||||
|
COPY kube-explorer-${OS}-${ARCH} /usr/bin/kube-explorer
|
||||||
ENTRYPOINT ["entrypoint.sh"]
|
ENTRYPOINT ["entrypoint.sh"]
|
||||||
|
103
scripts/build
103
scripts/build
@@ -1,52 +1,81 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
source $(dirname $0)/version
|
source "$(dirname $0)/version"
|
||||||
|
cd "$(dirname $0)/.."
|
||||||
|
|
||||||
|
rm -rf ./bin/* ./dist/*
|
||||||
|
|
||||||
OS_ARCH_ARG_LINUX="amd64 arm arm64"
|
OS_ARCH_ARG_LINUX="amd64 arm arm64"
|
||||||
OS_ARCH_ARG_DARWIN="amd64"
|
OS_ARCH_ARG_DARWIN="amd64 arm64"
|
||||||
|
OS_ARCH_ARG_WINDOWS="amd64"
|
||||||
|
|
||||||
LD_INJECT_VALUES="-X github.com/rancher/steve/pkg/version.Version=$VERSION
|
LD_INJECT_VALUES="-X github.com/cnrancher/kube-explorer/internal/version.Version=$VERSION
|
||||||
-X github.com/rancher/steve/pkg/version.GitCommit=$COMMIT"
|
-X github.com/cnrancher/kube-explorer/internal/version.GitCommit=$COMMIT"
|
||||||
|
|
||||||
[ "$(uname)" != "Darwin" ] && LINKFLAGS="-extldflags -static -s"
|
[ "$(uname)" != "Darwin" ] && LINKFLAGS="-extldflags -static -s"
|
||||||
|
|
||||||
pushd $GIT_SOURCE
|
case "$CROSS" in
|
||||||
|
"push")
|
||||||
|
for ARCH in ${OS_ARCH_ARG_LINUX}; do
|
||||||
|
OUTPUT_BIN="bin/kube-explorer-linux-$ARCH"
|
||||||
|
echo "Building binary for linux/$ARCH..."
|
||||||
|
GOARCH=$ARCH GOOS=linux CGO_ENABLED=0 go build -tags embed \
|
||||||
|
-ldflags \
|
||||||
|
"$LD_INJECT_VALUES $LINKFLAGS" \
|
||||||
|
-o ${OUTPUT_BIN}
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
"tag")
|
||||||
|
for ARCH in ${OS_ARCH_ARG_LINUX}; do
|
||||||
|
OUTPUT_BIN="bin/kube-explorer-linux-$ARCH"
|
||||||
|
echo "Building binary for linux/$ARCH..."
|
||||||
|
GOARCH=$ARCH GOOS=linux CGO_ENABLED=0 go build -tags embed \
|
||||||
|
-ldflags \
|
||||||
|
"$LD_INJECT_VALUES $LINKFLAGS" \
|
||||||
|
-o ${OUTPUT_BIN}
|
||||||
|
done
|
||||||
|
|
||||||
if [ -n "$CROSS" ]; then
|
for ARCH in ${OS_ARCH_ARG_DARWIN}; do
|
||||||
for ARCH in ${OS_ARCH_ARG_LINUX}; do
|
OUTPUT_BIN="bin/kube-explorer-darwin-$ARCH"
|
||||||
OUTPUT_BIN="bin/kube-explorer-linux-$ARCH"
|
echo "Building binary for darwin/$ARCH..."
|
||||||
echo "Building binary for linux/$ARCH..."
|
GOARCH=$ARCH GOOS=darwin CGO_ENABLED=0 go build -tags embed \
|
||||||
GOARCH=$ARCH GOOS=linux CGO_ENABLED=0 go build -tags embed \
|
-ldflags \
|
||||||
-ldflags \
|
"$LD_INJECT_VALUES" \
|
||||||
"$LD_INJECT_VALUES $LINKFLAGS" \
|
-o ${OUTPUT_BIN}
|
||||||
-o ${OUTPUT_BIN}
|
done
|
||||||
done
|
|
||||||
|
|
||||||
for ARCH in ${OS_ARCH_ARG_DARWIN}; do
|
for ARCH in ${OS_ARCH_ARG_WINDOWS}; do
|
||||||
OUTPUT_BIN="bin/kube-explorer-darwin-$ARCH"
|
OUTPUT_BIN="bin/kube-explorer-windows-$ARCH.exe"
|
||||||
echo "Building binary for darwin/$ARCH..."
|
echo "Building binary for windows/$ARCH..."
|
||||||
GOARCH=$ARCH GOOS=darwin CGO_ENABLED=0 go build -tags embed \
|
GOARCH=$ARCH GOOS=windows CGO_ENABLED=0 go build -tags embed \
|
||||||
-ldflags \
|
-ldflags \
|
||||||
"$LD_INJECT_VALUES" \
|
"$LD_INJECT_VALUES" \
|
||||||
-o ${OUTPUT_BIN}
|
-o ${OUTPUT_BIN}
|
||||||
done
|
done
|
||||||
else
|
;;
|
||||||
# only build one for current platform
|
*)
|
||||||
CGO_ENABLED=0 go build -tags embed \
|
# only build one for current platform
|
||||||
-ldflags \
|
CGO_ENABLED=0 go build -tags embed \
|
||||||
"$LD_INJECT_VALUES $LINKFLAGS" \
|
-ldflags \
|
||||||
-o bin/kube-explorer
|
"$LD_INJECT_VALUES $LINKFLAGS" \
|
||||||
fi
|
-o "bin/kube-explorer-$(uname | tr '[:upper:]' '[:lower:]')-${ARCH}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
for f in $(ls ./bin/); do
|
mkdir -p "./bin"
|
||||||
upx -o $DAPPER_SOURCE/bin/$f bin/$f || true
|
mkdir -p "./dist"
|
||||||
if [ -f $DAPPER_SOURCE/bin/$f ]; then
|
|
||||||
echo "UPX done!"
|
for f in ./bin/*; do
|
||||||
|
filename=$(basename "$f")
|
||||||
|
if [[ $filename != *darwin* && -z "$SKIP_COMPRESS" ]]; then
|
||||||
|
if upx -o "./dist/$filename" "$f"; then
|
||||||
|
echo "UPX done for $filename!"
|
||||||
|
else
|
||||||
|
echo "UPX failed for $filename, copying original file."
|
||||||
|
cp "$f" "./dist/$filename"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "Copy origin file as UPX failed!!!"
|
cp "$f" "./dist/$filename"
|
||||||
cp bin/$f $DAPPER_SOURCE/bin/$f
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
popd
|
|
||||||
|
@@ -6,13 +6,8 @@ cd $(dirname $0)
|
|||||||
|
|
||||||
[ "$(uname)" != "Darwin" ] && LINKFLAGS="-extldflags -static -s"
|
[ "$(uname)" != "Darwin" ] && LINKFLAGS="-extldflags -static -s"
|
||||||
|
|
||||||
pushd $GIT_SOURCE
|
|
||||||
|
|
||||||
CGO_ENABLED=0 go build \
|
CGO_ENABLED=0 go build \
|
||||||
-ldflags \
|
-ldflags \
|
||||||
"$LINKFLAGS" \
|
"$LINKFLAGS" \
|
||||||
-o bin/kube-explorer
|
-o bin/kube-explorer
|
||||||
|
|
||||||
mv bin/kube-explorer $DAPPER_SOURCE/bin/
|
|
||||||
|
|
||||||
popd
|
|
||||||
|
@@ -1,18 +1,18 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
mkdir -p $(dirname $GIT_SOURCE)
|
source $(dirname $0)/version
|
||||||
|
|
||||||
pushd $(dirname $GIT_SOURCE)
|
cd "$(dirname $0)/.." || exit 1;
|
||||||
|
|
||||||
git clone --depth=1 --branch ${GIT_BRANCH} https://github.com/niusmallnan/steve.git
|
if [[ "$(uname)" == "Darwin" ]]; then
|
||||||
cd steve
|
TAR_CMD="gtar"
|
||||||
git reset --hard ${GIT_COMMIT}
|
else
|
||||||
|
TAR_CMD="tar"
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p pkg/ui/ui/dashboard
|
mkdir -p internal/ui/ui/dashboard
|
||||||
cd pkg/ui/ui/dashboard
|
cd internal/ui/ui/dashboard || exit 1;
|
||||||
curl -sL https://pandaria-dashboard-ui.s3.ap-southeast-2.amazonaws.com/release-2.6-cn/kube-explorer-ui/${CATTLE_DASHBOARD_UI_VERSION}.tar.gz | tar xvzf - --strip-components=2
|
curl -sL https://pandaria-dashboard-ui.s3.ap-southeast-2.amazonaws.com/release-2.8-cn/kube-explorer-ui/${CATTLE_DASHBOARD_UI_VERSION}.tar.gz | $TAR_CMD xvzf - --strip-components=2
|
||||||
cp index.html ../index.html
|
cp index.html ../index.html
|
||||||
|
|
||||||
popd
|
|
||||||
|
|
||||||
$(dirname $0)/hack_fs $GIT_SOURCE/pkg/ui/ui/
|
|
||||||
|
@@ -2,10 +2,11 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
mkdir -p bin dist
|
mkdir -p bin dist
|
||||||
if [ -e ./scripts/$1 ]; then
|
if [ -e "./scripts/$1" ]; then
|
||||||
./scripts/"$@"
|
./scripts/"$@"
|
||||||
else
|
else
|
||||||
exec "$@"
|
exec "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chown -R $DAPPER_UID:$DAPPER_GID .
|
|
||||||
|
chown -R "$DAPPER_UID:$DAPPER_GID" .
|
||||||
|
9
scripts/github_ci
Executable file
9
scripts/github_ci
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd $(dirname $0)
|
||||||
|
|
||||||
|
./download
|
||||||
|
./validate
|
||||||
|
./build
|
||||||
|
|
@@ -1,42 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
#
|
|
||||||
# find . -type f -name "_*"
|
|
||||||
#
|
|
||||||
function hack_files() {
|
|
||||||
for f in $(find $1 -type f -name "_*"); do
|
|
||||||
name=$(basename $f)
|
|
||||||
updir=$(dirname $f)
|
|
||||||
new_path=$updir/${name:1}
|
|
||||||
echo "move $f $new_path"
|
|
||||||
mv $f $new_path
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# find . -type d -name "_*"
|
|
||||||
#
|
|
||||||
function hack_dirs() {
|
|
||||||
for d in $(find $1 -mindepth 1 -maxdepth 1 -type d); do
|
|
||||||
if [[ ! -d $d ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
name=$(basename $d)
|
|
||||||
if [[ ${name:0:1} == "_" ]]; then
|
|
||||||
updir=$(dirname $d)
|
|
||||||
new_path=$updir/${name:1}
|
|
||||||
echo "move $d $new_path"
|
|
||||||
mv $d $new_path
|
|
||||||
hack_dirs $new_path
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
hack_dirs $d
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
pushd $1
|
|
||||||
hack_files .
|
|
||||||
hack_dirs .
|
|
||||||
popd
|
|
@@ -2,17 +2,8 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
source $(dirname $0)/version
|
source $(dirname $0)/version
|
||||||
|
cd "$(dirname $0)/.."
|
||||||
|
|
||||||
pushd $DAPPER_SOURCE
|
cp dist/* package/
|
||||||
|
docker build -f package/Dockerfile -t "cnrancher/kube-explorer:$VERSION" package
|
||||||
|
|
||||||
if [ -f bin/kube-explorer-linux-${ARCH} ]; then
|
|
||||||
# For cross mode
|
|
||||||
cp bin/kube-explorer-linux-${ARCH} package/kube-explorer
|
|
||||||
else
|
|
||||||
# For common mode
|
|
||||||
cp bin/kube-explorer package/
|
|
||||||
fi
|
|
||||||
cd package
|
|
||||||
docker build -f Dockerfile -t cnrancher/kube-explorer:$VERSION .
|
|
||||||
|
|
||||||
popd
|
|
||||||
|
42
scripts/release-note
Executable file
42
scripts/release-note
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source "$(dirname $0)/version"
|
||||||
|
cd "$(dirname $0)/.."
|
||||||
|
|
||||||
|
mkdir -p dist
|
||||||
|
TARGET_PATH="dist/release-note"
|
||||||
|
|
||||||
|
if [ -z "$(command -v release-notary)" ]; then
|
||||||
|
echo "release-notary is not found, skip generating release notes."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${GIT_TAG}" ]; then
|
||||||
|
echo "running this scrpit without tag, skip generating release notes."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
GIT_TAG=$(echo "${GIT_TAG}" | grep -E "^v([0-9]+)\.([0-9]+)(\.[0-9]+)?(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$") || true
|
||||||
|
|
||||||
|
if [ "${GIT_TAG}" = "" ]; then
|
||||||
|
echo "git GIT_TAG is not validated, skip generating release notes."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for tag in $(git tag -l --sort=-v:refname); do
|
||||||
|
if [ "${tag}" = "${GIT_TAG}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
filterred=$(echo "${tag}" | grep -E "^v([0-9]+)\.([0-9]+)(\.[0-9]+)?(-rc[0-9]*)$") || true
|
||||||
|
if [ "${filterred}" = "" ]; then
|
||||||
|
echo "get real release tag ${tag}, stopping untag"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
git tag -d ${tag}
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "following release notes will be published..."
|
||||||
|
release-notary publish -d 2>/dev/null | sed '1d' | sed '$d' > $TARGET_PATH
|
||||||
|
cat "$TARGET_PATH"
|
@@ -1,7 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
source $(dirname $0)/version
|
||||||
|
|
||||||
pushd $GIT_SOURCE
|
cd "$(dirname $0)/.."
|
||||||
|
|
||||||
if ! command -v golangci-lint; then
|
if ! command -v golangci-lint; then
|
||||||
echo Running: go fmt
|
echo Running: go fmt
|
||||||
@@ -9,13 +10,11 @@ if ! command -v golangci-lint; then
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
#echo Running: golangci-lint
|
echo Running: golangci-lint
|
||||||
#golangci-lint run
|
golangci-lint run
|
||||||
|
|
||||||
echo Tidying up modules
|
echo Tidying up modules
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
||||||
echo Verifying modules
|
echo Verifying modules
|
||||||
go mod verify
|
go mod verify
|
||||||
|
|
||||||
popd
|
|
||||||
|
Reference in New Issue
Block a user