Compare commits

...

38 Commits

Author SHA1 Message Date
github-actions[bot]
15bea4fb64 Fix: fail to query the application logs with the special characters (#4308)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 0101396a42)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-07-01 20:15:09 +08:00
github-actions[bot]
1a094a4eea Fix: Jfrog Webhook Handler Cannot Get Right Image (#4306)
Merge branch 'release-1.4'

Apply suggestions from code review

Co-authored-by: lqs429521992 <lqs429521992@qq.com>

Update webhook.go

Fix: format

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit b0d0a81731)

Co-authored-by: qingsliu <lqs429521992@qq.com>
2022-07-01 20:08:40 +08:00
github-actions[bot]
65b6f47330 Fix: fix the goroutine leak in http request (#4304)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 559ef83abd)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-07-01 17:57:34 +08:00
github-actions[bot]
3a4cd2dca6 Fix: kube apply ignore userinfo for rt (#4300)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 2a9e741d4c)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-07-01 17:32:52 +08:00
github-actions[bot]
9fabd950e5 Chore: avoid update version file when publish smaller version (#4287)
Signed-off-by: qiaozp <chivalry.pp@gmail.com>
(cherry picked from commit 696234e1aa)

Co-authored-by: qiaozp <chivalry.pp@gmail.com>
2022-06-30 15:51:17 +08:00
github-actions[bot]
0a012b4d34 Feat: enhance ServiceAccount trait to support privileges (#4278)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit f8d4aca499)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-06-29 15:00:32 +08:00
github-actions[bot]
7c231e6c48 Fix: support stdin and url for vela ql (#4277)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 235a4471c0)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-06-29 14:53:26 +08:00
github-actions[bot]
36b6c3e7b5 Fix: trim quot char for velaql output (#4269)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit fdd0c93dba)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-06-27 16:52:26 +08:00
github-actions[bot]
4cc019722c [Backport release-1.4] Feat: enhance velq ql and support cue file (#4266)
* Feat: enhance velq ql and support cue file

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 70ed417326)

* add statement

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 25260eb9ef)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-06-27 16:17:21 +08:00
github-actions[bot]
b040ae65da [Backport release-1.4] Fix: provider can't be added since 1.4 as context abused && Feat: add cache for remote terraform module in vela show (#4263)
* Fix: provider can't be added since 1.4 as context abused

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit b05fb26418)

* Feat: add cache for remote terraform module in vela show

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 4722028530)

* Fix: add message for terraform resource in error state

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 438145b12e)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-06-27 11:32:20 +08:00
github-actions[bot]
f0fb4ed099 Feat: omit service output if there's nothing (#4262)
Signed-off-by: Sumit Tembe <sumit.tembe@outlook.com>
(cherry picked from commit 8911143389)

Co-authored-by: Sumit Tembe <sumit.tembe@outlook.com>
2022-06-27 10:31:49 +08:00
github-actions[bot]
7f89d12059 environment from configmap or secret not mandatory in task and crontask componentdefinition (#4254)
Signed-off-by: Carmendelope <carmen@napptive.com>
(cherry picked from commit 35f130ea08)

Co-authored-by: Carmendelope <carmen@napptive.com>
2022-06-25 09:00:24 +08:00
github-actions[bot]
3c61bcb8f0 Fix: vela status print wrong STATUS (#4249)
Signed-off-by: StevenLeiZhang <zhangleiic@163.com>
(cherry picked from commit 5f4616a453)

Co-authored-by: StevenLeiZhang <zhangleiic@163.com>
2022-06-24 17:16:58 +08:00
github-actions[bot]
a14b536fd1 [Backport release-1.4] Feat: skip validating version check (#4247)
* skip validating version check

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 4b94a967ae)

* add comments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit bbdc19c472)

* fix comments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit b5c705013d)

* fix test

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit adff7ef309)

* fix commments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit cf4afee0d8)

* add compatible logic for old controller

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 3f5a6d33b9)

* modify minimal

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit b020725112)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-06-24 14:13:17 +08:00
github-actions[bot]
ba5a726854 [Backport release-1.4] Fix: fixed the problems of display definition in web and support displaying WorkflowStep and Policy (#4241)
* Fix: fixed the problems of display definition in web

Some ComponentDefinitions, TraitDefinitions, WorkflowDefinitions
failed to show the usage in web browser

Signed-off-by: Zheng Xi Zhou <zhengxi.zzx@alibaba-inc.com>
(cherry picked from commit c8985ccc3e)

* set printable type for {}

Signed-off-by: Zheng Xi Zhou <zhengxi.zzx@alibaba-inc.com>
(cherry picked from commit 87efb357b2)

* support WorkflowSteps and Policies

Signed-off-by: Zheng Xi Zhou <zhengxi.zzx@alibaba-inc.com>
(cherry picked from commit 8fb83152bc)

Co-authored-by: Zheng Xi Zhou <zhengxi.zzx@alibaba-inc.com>
2022-06-23 19:46:51 +08:00
github-actions[bot]
ffb9d06427 Fix: json-patch & json-merge-patch open result (#4229)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 4ea3ca8d5b)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-06-22 20:05:19 +08:00
barnettZQG
819dc26ace Chore: change the acr registry address (#4214) (#4216)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
2022-06-22 14:46:13 +08:00
github-actions[bot]
5e3ab732df [Backport release-1.4] Feat: add the API for querying the image info (#4211)
* Feat: add the API for querying the image info

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit e257cc6658)

* Fix: the code style

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 45e183b44b)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-06-21 15:05:15 +08:00
github-actions[bot]
62d5507499 Fix: cue patch remove temp var (#4208)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 660e89b3a0)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-06-20 17:38:34 +08:00
github-actions[bot]
c0daf688a6 Fix: clear namespace for cluster scoped resource for dispatching (#4195)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 7c9de1b071)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-06-16 14:50:44 +08:00
github-actions[bot]
48d19a2427 [Backport release-1.4] Feat: cli addon add registry add more git types (#4191)
* add more git types

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 1d6a5eea22)

* fix comments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix comments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 91122432ec)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-06-16 12:23:15 +08:00
github-actions[bot]
4da8d49e60 Fix: fix the annotation for APIService (#4190)
Make the annatation for cert-manger be the same as the new version
secret

Signed-off-by: Zheng Xi Zhou <zhengxi.zzx@alibaba-inc.com>
(cherry picked from commit 62ef7b7f99)

Co-authored-by: Zheng Xi Zhou <zhengxi.zzx@alibaba-inc.com>
2022-06-16 11:02:35 +08:00
github-actions[bot]
4db9e89816 Fix: enhance CLI for managing OCM clusters (#4178)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 0662e2a79d)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-06-15 11:23:12 +08:00
github-actions[bot]
667053409d Fix: fix trait customStatus error when controlPlanOnly=true (#4177)
Signed-off-by: fourierr <maxiangboo@qq.com>
(cherry picked from commit 38648fcf30)

Co-authored-by: fourierr <maxiangboo@qq.com>
2022-06-15 11:22:15 +08:00
github-actions[bot]
eb9ddaabd3 [Backport release-1.4] Feat: optimize the API that list and detail definition (#4160)
* Fix: ignore the error that the definition API schema is not exist

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 855716d989)

* Fix: disable the cache when listing the definitions

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit f25882974d)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-06-13 13:25:41 +08:00
github-actions[bot]
f11a94612f Feat: support insecure cluster (#4159)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 8abedf3005)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-06-13 13:15:34 +08:00
github-actions[bot]
56f9d7cb9c [Backport release-1.4] Fix: mongoDB datastore can't list special email user(#4104) (#4148)
* Add description column to vela trait and component command (#4107)

Signed-off-by: Holger Protzek <holger.protzek@springernature.com>
Signed-off-by: fengkang <fengkangb@digitalchina.com>
(cherry picked from commit 9c57ae2a15)

* Fix: mongoDB datastore can't list special email user(#4104)

Signed-off-by: fengkang <fengkangb@digitalchina.com>
(cherry picked from commit ec07790935)

* Fix: mongoDB datastore can't list special email user(#4104)
     change the function name from verifyUserValue to verifyValue
     add test case to test kubeapi.go:87

Signed-off-by: fengkang <fengkangb@digitalchina.com>
(cherry picked from commit 4c55e0688f)

* Fix: mongoDB datastore can't list special email user(#4104)
     change the function name from verifyUserValue to verifyValue
     add test case to test kubeapi.go:87
     add delete test case

Signed-off-by: fengkang <fengkangb@digitalchina.com>
(cherry picked from commit b2e76001c9)

* Fix: mongoDB datastore can't list special email user(#4104)
     optimize the test case

Signed-off-by: fengkang <fengkangb@digitalchina.com>
(cherry picked from commit a63c96fae1)

* Fix: mongoDB datastore can't list special email user(#4104)
     optimize the test case use user
     change all verify timing in kubeapi

Signed-off-by: fengkang <fengkangb@digitalchina.com>
(cherry picked from commit 2ede4f7b0c)

* Fix: mongoDB datastore can't list special email user(#4104)

Signed-off-by: fengkang <fengkangb@digitalchina.com>
(cherry picked from commit 356150642f)

* Fix: mongoDB datastore can't list special email user(#4104)

Signed-off-by: fengkang <fengkangb@digitalchina.com>
(cherry picked from commit 48a9e55937)

Co-authored-by: Holger Protzek <3481523+hprotzek@users.noreply.github.com>
Co-authored-by: fengkang01 <fengkangb@digitalchina.com>
2022-06-10 15:33:53 +08:00
github-actions[bot]
fbbc666019 [Backport release-1.4] Fix: api not exist don't break whole query process (#4137)
* make resource tree more

resourceTree more robust

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 327ec35cf4)

* log the error

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 1136afe78c)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-06-09 11:36:57 +08:00
github-actions[bot]
d0788254cb Fix: vela addon registry get panic (#4136)
Signed-off-by: ZhongsJie <zhongsjie@gmail.com>
(cherry picked from commit 0d329e394e)

Co-authored-by: ZhongsJie <zhongsjie@gmail.com>
2022-06-09 10:19:28 +08:00
github-actions[bot]
c72a6aef87 Chore(deps): Bump github.com/containerd/containerd from 1.5.10 to 1.5.13 (#4125)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.5.10 to 1.5.13.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.5.10...v1.5.13)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 9982c7ceaa)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-08 12:56:05 +08:00
github-actions[bot]
195b7fe0c7 [Backport release-1.4] Fix: bump oamdev/kube-webhook-certgen to v2.4.1 to support arm64 (#4117)
* Fix: split the image build process to make it faster

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 339977e874)

* Fix: bump oamdev/kube-webhook-certgen to v2.4.1 to support arm64

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit dc054e2ce2)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-06-05 15:10:48 +08:00
github-actions[bot]
33c9e3b170 Fix: change the image name in ghcr to align with docker image registry (#4111)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 71776c374b)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-06-04 14:19:21 +08:00
github-actions[bot]
ea0508a634 [Backport release-1.4] Fix: hold the force uninstalling process untill the last addon been deleted (#4103)
* hold the force uninstalling process untill the last addon been deleted

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix lint

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix comments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix comments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 1d4266432c)

* fix comments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix lint

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit cecd8c28d0)

* add period

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 62e4dac538)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-06-02 16:29:02 +08:00
github-actions[bot]
23e29aa62a Fix: vela provider delete command's example is wrong (#4099)
Signed-off-by: StevenLeiZhang <zhangleiic@163.com>
(cherry picked from commit 9b66f90100)

Co-authored-by: StevenLeiZhang <zhangleiic@163.com>
2022-06-02 11:04:34 +08:00
github-actions[bot]
ed2cb80219 Fix: the new default values do not take effect when upgrading the vela core (#4097)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 0ccbbb2636)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-06-02 10:23:30 +08:00
github-actions[bot]
1a3d5debd5 Fix: show the default password (#4095)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit c3cfc4729e)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-06-02 10:14:43 +08:00
github-actions[bot]
d4a82fe292 Fix: load the provider subcommands on demand (#4090)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 52bbf937bb)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-06-01 16:32:53 +08:00
github-actions[bot]
963ae400fa Feat: use deferred config in CLI (#4085)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 478434003b)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-06-01 10:24:57 +08:00
105 changed files with 2063 additions and 1157 deletions

View File

@@ -15,7 +15,7 @@ env:
ARTIFACT_HUB_REPOSITORY_ID: ${{ secrets.ARTIFACT_HUB_REPOSITORY_ID }}
jobs:
publish-images:
publish-core-images:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
@@ -47,20 +47,16 @@ jobs:
- name: Login Alibaba Cloud ACR
uses: docker/login-action@v1
with:
registry: kubevela-registry.cn-hangzhou.cr.aliyuncs.com
username: ${{ secrets.ACR_USERNAME }}@aliyun-inner.com
registry: ${{ secrets.ACR_DOMAIN }}
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
with:
driver-opts: image=moby/buildkit:master
- name: Build & Pushing vela-core for ACR
run: |
docker build --build-arg GOPROXY=https://proxy.golang.org --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} --build-arg GITVERSION=git-${{ steps.vars.outputs.git_revision }} -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }} .
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
- uses: docker/build-push-action@v2
name: Build & Pushing vela-core for Dockerhub and GHCR
name: Build & Pushing vela-core for Dockerhub, GHCR and ACR
with:
context: .
file: Dockerfile
@@ -75,36 +71,11 @@ jobs:
GOPROXY=https://proxy.golang.org
tags: |-
docker.io/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository }}/vela-core:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
${{ secrets.ACR_DOMAIN }}/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
- name: Build & Pushing vela-apiserver for ACR
run: |
docker build --build-arg GOPROXY=https://proxy.golang.org --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} --build-arg GITVERSION=git-${{ steps.vars.outputs.git_revision }} -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }} -f Dockerfile.apiserver .
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
- uses: docker/build-push-action@v2
name: Build & Pushing vela-apiserver for Dockerhub and GHCR
with:
context: .
file: Dockerfile.apiserver
labels: |-
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
build-args: |
GITVERSION=git-${{ steps.vars.outputs.git_revision }}
VERSION=${{ steps.get_version.outputs.VERSION }}
GOPROXY=https://proxy.golang.org
tags: |-
docker.io/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository }}/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
- name: Build & Pushing vela CLI for ACR
run: |
docker build --build-arg GOPROXY=https://proxy.golang.org --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} --build-arg GITVERSION=git-${{ steps.vars.outputs.git_revision }} -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }} -f Dockerfile.cli .
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
- uses: docker/build-push-action@v2
name: Build & Pushing CLI for Dockerhub and GHCR
name: Build & Pushing CLI for Dockerhub, GHCR and ACR
with:
context: .
file: Dockerfile.cli
@@ -119,10 +90,70 @@ jobs:
GOPROXY=https://proxy.golang.org
tags: |-
docker.io/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository }}/vela-cli:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
${{ secrets.ACR_DOMAIN }}/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
publish-addon-images:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Get the version
id: get_version
run: |
VERSION=${GITHUB_REF#refs/tags/}
if [[ ${GITHUB_REF} == "refs/heads/master" ]]; then
VERSION=latest
fi
echo ::set-output name=VERSION::${VERSION}
- name: Get git revision
id: vars
shell: bash
run: |
echo "::set-output name=git_revision::$(git rev-parse --short HEAD)"
- name: Login ghcr.io
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login docker.io
uses: docker/login-action@v1
with:
registry: docker.io
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login Alibaba Cloud ACR
uses: docker/login-action@v1
with:
registry: ${{ secrets.ACR_DOMAIN }}
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
with:
driver-opts: image=moby/buildkit:master
- uses: docker/build-push-action@v2
name: Build & Pushing runtime rollout for Dockerhub and GHCR
name: Build & Pushing vela-apiserver for Dockerhub, GHCR and ACR
with:
context: .
file: Dockerfile.apiserver
labels: |-
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
build-args: |
GITVERSION=git-${{ steps.vars.outputs.git_revision }}
VERSION=${{ steps.get_version.outputs.VERSION }}
GOPROXY=https://proxy.golang.org
tags: |-
docker.io/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
${{ secrets.ACR_DOMAIN }}/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
- uses: docker/build-push-action@v2
name: Build & Pushing runtime rollout Dockerhub, GHCR and ACR
with:
context: .
file: runtime/rollout/Dockerfile
@@ -137,8 +168,8 @@ jobs:
GOPROXY=https://proxy.golang.org
tags: |-
docker.io/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository }}/vela-rollout:${{ steps.get_version.outputs.VERSION }}
kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
${{ secrets.ACR_DOMAIN }}/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
publish-charts:
env:

View File

@@ -123,6 +123,11 @@ jobs:
- name: sync the latest version file
if: ${{ !contains(env.VELA_VERSION,'alpha') && !contains(env.VELA_VERSION,'beta') }}
run: |
LATEST_VERSION=$(curl -fsSl https://static.kubevela.net/binary/vela/latest_version)
verlte() {
[ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ]
}
verlte ${{ env.VELA_VERSION }} $LATEST_VERSION && echo "${{ env.VELA_VERSION }} <= $LATEST_VERSION, skip update" && exit 0
echo ${{ env.VELA_VERSION }} > ./latest_version
./ossutil --config-file .ossutilconfig cp -u ./latest_version oss://$BUCKET/binary/vela/latest_version

View File

@@ -106,7 +106,7 @@ metadata:
name: v1alpha1.cluster.core.oam.dev
annotations:
{{- if and .Values.multicluster.clusterGateway.secureTLS.enabled .Values.multicluster.clusterGateway.secureTLS.certManager.enabled }}
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kubevela.fullname" . }}-cluster-gateway-tls"
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kubevela.fullname" . }}-cluster-gateway-tls-v2"
{{- end }}
labels:
api: cluster-extension-apiserver

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: affinity specify affinity and tolerationon K8s pod for your workload which follows the pod spec in path 'spec.template'.
definition.oam.dev/description: Affinity specifies affinity and toleration K8s pod for your workload which follows the pod spec in path 'spec.template'.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: affinity

View File

@@ -20,6 +20,7 @@ spec:
import (
"encoding/base64"
"encoding/json"
"strconv"
)
output: {
@@ -42,21 +43,29 @@ spec:
if parameter.auth == _|_ {
type: "Opaque"
}
if parameter.auth != _|_ {
stringData: ".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
stringData: {
if parameter.auth != _|_ && parameter.auth.username != _|_ {
".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
})
})
}
if parameter.insecure != _|_ {
"insecure-skip-verify": strconv.FormatBool(parameter.insecure)
}
if parameter.useHTTP != _|_ {
"protocol-use-http": strconv.FormatBool(parameter.useHTTP)
}
}
}
parameter: {
// +usage=Image registry FQDN
// +usage=Image registry FQDN, such as: index.docker.io
registry: string
// +usage=Authenticate the image registry
auth?: {
@@ -67,6 +76,10 @@ spec:
// +usage=Private Image registry email
email?: string
}
// +usage=For the registry server that uses the self-signed certificate
insecure?: bool
// +usage=For the registry server that uses the HTTP protocol
useHTTP?: bool
}
workload:
type: autodetects.core.oam.dev

View File

@@ -196,14 +196,14 @@ spec:
// +usage=Specifies a source the value of this var should come from
valueFrom?: {
// +usage=Selects a key of a secret in the pod's namespace
secretKeyRef: {
secretKeyRef?: {
// +usage=The name of the secret in the pod's namespace to select from
name: string
// +usage=The key of the secret to select from. Must be a valid secret key
key: string
}
// +usage=Selects a key of a config map in the pod's namespace
configMapKeyRef: {
configMapKeyRef?: {
// +usage=The name of the config map in the pod's namespace to select from
name: string
// +usage=The key of the config map to select from. Must be a valid secret key

View File

@@ -14,10 +14,114 @@ spec:
schematic:
cue:
template: |
#Privileges: {
// +usage=Specify the verbs to be allowed for the resource
verbs: [...string]
// +usage=Specify the apiGroups of the resource
apiGroups?: [...string]
// +usage=Specify the resources to be allowed
resources?: [...string]
// +usage=Specify the resourceNames to be allowed
resourceNames?: [...string]
// +usage=Specify the resource url to be allowed
nonResourceURLs?: [...string]
// +usage=Specify the scope of the privileges, default to be namespace scope
scope: *"namespace" | "cluster"
}
parameter: {
// +usage=Specify the name of ServiceAccount
name: string
// +usage=Specify whether to create new ServiceAccount or not
create: *false | bool
// +usage=Specify the privileges of the ServiceAccount, if not empty, RoleBindings(ClusterRoleBindings) will be created
privileges?: [...#Privileges]
}
// +patchStrategy=retainKeys
patch: spec: template: spec: serviceAccountName: parameter.name
_clusterPrivileges: [ for p in parameter.privileges if p.scope == "cluster" {p}]
_namespacePrivileges: [ for p in parameter.privileges if p.scope == "namespace" {p}]
outputs: {
if parameter.create {
"service-account": {
apiVersion: "v1"
kind: "ServiceAccount"
metadata: name: parameter.name
}
}
if parameter.privileges != _|_ {
if len(_clusterPrivileges) > 0 {
"cluster-role": {
apiVersion: "rbac.authorization.k8s.io/v1"
kind: "ClusterRole"
metadata: name: "\(context.namespace):\(parameter.name)"
rules: [ for p in _clusterPrivileges {
verbs: p.verbs
if p.apiGroups != _|_ {
apiGroups: p.apiGroups
}
if p.resources != _|_ {
resources: p.resources
}
if p.resourceNames != _|_ {
resources: p.resourceNames
}
if p.nonResourceURLs != _|_ {
nonResourceURLs: p.nonResourceURLs
}
}]
}
"cluster-role-binding": {
apiVersion: "rbac.authorization.k8s.io/v1"
kind: "ClusterRoleBinding"
metadata: name: "\(context.namespace):\(parameter.name)"
roleRef: {
apiGroup: "rbac.authorization.k8s.io"
kind: "ClusterRole"
name: "\(context.namespace):\(parameter.name)"
}
subjects: [{
kind: "ServiceAccount"
name: parameter.name
namespace: "\(context.namespace)"
}]
}
}
if len(_namespacePrivileges) > 0 {
role: {
apiVersion: "rbac.authorization.k8s.io/v1"
kind: "Role"
metadata: name: parameter.name
rules: [ for p in _namespacePrivileges {
verbs: p.verbs
if p.apiGroups != _|_ {
apiGroups: p.apiGroups
}
if p.resources != _|_ {
resources: p.resources
}
if p.resourceNames != _|_ {
resources: p.resourceNames
}
if p.nonResourceURLs != _|_ {
nonResourceURLs: p.nonResourceURLs
}
}]
}
"role-binding": {
apiVersion: "rbac.authorization.k8s.io/v1"
kind: "RoleBinding"
metadata: name: parameter.name
roleRef: {
apiGroup: "rbac.authorization.k8s.io"
kind: "Role"
name: parameter.name
}
subjects: [{
kind: "ServiceAccount"
name: parameter.name
}]
}
}
}
}

View File

@@ -149,14 +149,14 @@ spec:
// +usage=Specifies a source the value of this var should come from
valueFrom?: {
// +usage=Selects a key of a secret in the pod's namespace
secretKeyRef: {
secretKeyRef?: {
// +usage=The name of the secret in the pod's namespace to select from
name: string
// +usage=The key of the secret to select from. Must be a valid secret key
key: string
}
// +usage=Selects a key of a config map in the pod's namespace
configMapKeyRef: {
configMapKeyRef?: {
// +usage=The name of the config map in the pod's namespace to select from
name: string
// +usage=The key of the config map to select from. Must be a valid secret key

View File

@@ -122,6 +122,7 @@ metadata:
name: {{ include "kubevela.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
controller.oam.dev/name: vela-core
{{- include "kubevela.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}

View File

@@ -226,7 +226,7 @@ admissionWebhooks:
enabled: true
image:
repository: oamdev/kube-webhook-certgen
tag: v2.4.0
tag: v2.4.1
pullPolicy: IfNotPresent
nodeSelector: {}
affinity: {}

View File

@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: affinity specify affinity and tolerationon K8s pod for your workload which follows the pod spec in path 'spec.template'.
definition.oam.dev/description: Affinity specifies affinity and toleration K8s pod for your workload which follows the pod spec in path 'spec.template'.
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: affinity

View File

@@ -20,6 +20,7 @@ spec:
import (
"encoding/base64"
"encoding/json"
"strconv"
)
output: {
@@ -42,21 +43,29 @@ spec:
if parameter.auth == _|_ {
type: "Opaque"
}
if parameter.auth != _|_ {
stringData: ".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
stringData: {
if parameter.auth != _|_ && parameter.auth.username != _|_ {
".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
})
})
}
if parameter.insecure != _|_ {
"insecure-skip-verify": strconv.FormatBool(parameter.insecure)
}
if parameter.useHTTP != _|_ {
"protocol-use-http": strconv.FormatBool(parameter.useHTTP)
}
}
}
parameter: {
// +usage=Image registry FQDN
// +usage=Image registry FQDN, such as: index.docker.io
registry: string
// +usage=Authenticate the image registry
auth?: {
@@ -67,6 +76,10 @@ spec:
// +usage=Private Image registry email
email?: string
}
// +usage=For the registry server that uses the self-signed certificate
insecure?: bool
// +usage=For the registry server that uses the HTTP protocol
useHTTP?: bool
}
workload:
type: autodetects.core.oam.dev

View File

@@ -196,14 +196,14 @@ spec:
// +usage=Specifies a source the value of this var should come from
valueFrom?: {
// +usage=Selects a key of a secret in the pod's namespace
secretKeyRef: {
secretKeyRef?: {
// +usage=The name of the secret in the pod's namespace to select from
name: string
// +usage=The key of the secret to select from. Must be a valid secret key
key: string
}
// +usage=Selects a key of a config map in the pod's namespace
configMapKeyRef: {
configMapKeyRef?: {
// +usage=The name of the config map in the pod's namespace to select from
name: string
// +usage=The key of the config map to select from. Must be a valid secret key

View File

@@ -14,10 +14,114 @@ spec:
schematic:
cue:
template: |
#Privileges: {
// +usage=Specify the verbs to be allowed for the resource
verbs: [...string]
// +usage=Specify the apiGroups of the resource
apiGroups?: [...string]
// +usage=Specify the resources to be allowed
resources?: [...string]
// +usage=Specify the resourceNames to be allowed
resourceNames?: [...string]
// +usage=Specify the resource url to be allowed
nonResourceURLs?: [...string]
// +usage=Specify the scope of the privileges, default to be namespace scope
scope: *"namespace" | "cluster"
}
parameter: {
// +usage=Specify the name of ServiceAccount
name: string
// +usage=Specify whether to create new ServiceAccount or not
create: *false | bool
// +usage=Specify the privileges of the ServiceAccount, if not empty, RoleBindings(ClusterRoleBindings) will be created
privileges?: [...#Privileges]
}
// +patchStrategy=retainKeys
patch: spec: template: spec: serviceAccountName: parameter.name
_clusterPrivileges: [ for p in parameter.privileges if p.scope == "cluster" {p}]
_namespacePrivileges: [ for p in parameter.privileges if p.scope == "namespace" {p}]
outputs: {
if parameter.create {
"service-account": {
apiVersion: "v1"
kind: "ServiceAccount"
metadata: name: parameter.name
}
}
if parameter.privileges != _|_ {
if len(_clusterPrivileges) > 0 {
"cluster-role": {
apiVersion: "rbac.authorization.k8s.io/v1"
kind: "ClusterRole"
metadata: name: "\(context.namespace):\(parameter.name)"
rules: [ for p in _clusterPrivileges {
verbs: p.verbs
if p.apiGroups != _|_ {
apiGroups: p.apiGroups
}
if p.resources != _|_ {
resources: p.resources
}
if p.resourceNames != _|_ {
resources: p.resourceNames
}
if p.nonResourceURLs != _|_ {
nonResourceURLs: p.nonResourceURLs
}
}]
}
"cluster-role-binding": {
apiVersion: "rbac.authorization.k8s.io/v1"
kind: "ClusterRoleBinding"
metadata: name: "\(context.namespace):\(parameter.name)"
roleRef: {
apiGroup: "rbac.authorization.k8s.io"
kind: "ClusterRole"
name: "\(context.namespace):\(parameter.name)"
}
subjects: [{
kind: "ServiceAccount"
name: parameter.name
namespace: "\(context.namespace)"
}]
}
}
if len(_namespacePrivileges) > 0 {
role: {
apiVersion: "rbac.authorization.k8s.io/v1"
kind: "Role"
metadata: name: parameter.name
rules: [ for p in _namespacePrivileges {
verbs: p.verbs
if p.apiGroups != _|_ {
apiGroups: p.apiGroups
}
if p.resources != _|_ {
resources: p.resources
}
if p.resourceNames != _|_ {
resources: p.resourceNames
}
if p.nonResourceURLs != _|_ {
nonResourceURLs: p.nonResourceURLs
}
}]
}
"role-binding": {
apiVersion: "rbac.authorization.k8s.io/v1"
kind: "RoleBinding"
metadata: name: parameter.name
roleRef: {
apiGroup: "rbac.authorization.k8s.io"
kind: "Role"
name: parameter.name
}
subjects: [{
kind: "ServiceAccount"
name: parameter.name
}]
}
}
}
}

View File

@@ -149,14 +149,14 @@ spec:
// +usage=Specifies a source the value of this var should come from
valueFrom?: {
// +usage=Selects a key of a secret in the pod's namespace
secretKeyRef: {
secretKeyRef?: {
// +usage=The name of the secret in the pod's namespace to select from
name: string
// +usage=The key of the secret to select from. Must be a valid secret key
key: string
}
// +usage=Selects a key of a config map in the pod's namespace
configMapKeyRef: {
configMapKeyRef?: {
// +usage=The name of the config map in the pod's namespace to select from
name: string
// +usage=The key of the config map to select from. Must be a valid secret key

View File

@@ -125,6 +125,7 @@ metadata:
name: {{ include "kubevela.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
controller.oam.dev/name: vela-core
{{- include "kubevela.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}

View File

@@ -203,7 +203,7 @@ admissionWebhooks:
enabled: true
image:
repository: oamdev/kube-webhook-certgen
tag: v2.4.0
tag: v2.4.1
pullPolicy: IfNotPresent
nodeSelector: {}
affinity: {}

6
go.mod
View File

@@ -16,7 +16,7 @@ require (
github.com/barnettZQG/inject v0.0.1
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
github.com/briandowns/spinner v1.11.1
github.com/containerd/containerd v1.5.10
github.com/containerd/containerd v1.5.13
github.com/coreos/go-oidc v2.1.0+incompatible
github.com/coreos/prometheus-operator v0.41.1
github.com/crossplane/crossplane-runtime v0.14.1-0.20210722005935-0b469fcc77cd
@@ -46,7 +46,7 @@ require (
github.com/hashicorp/hcl/v2 v2.9.1
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174
github.com/imdario/mergo v0.3.12
github.com/kubevela/prism v1.4.0
github.com/kubevela/prism v1.4.1-0.20220613123457-94f1190f87c2
github.com/kyokomi/emoji v2.2.4+incompatible
github.com/mitchellh/hashstructure/v2 v2.0.1
github.com/oam-dev/cluster-gateway v1.4.0
@@ -122,7 +122,7 @@ require (
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
github.com/Masterminds/squirrel v1.5.2 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Microsoft/hcsshim v0.8.23 // indirect
github.com/Microsoft/hcsshim v0.8.24 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect

15
go.sum
View File

@@ -178,8 +178,8 @@ github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
github.com/Microsoft/hcsshim v0.8.23 h1:47MSwtKGXet80aIn+7h4YI6fwPmwIghAnsx2aOUrG2M=
github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=
github.com/Microsoft/hcsshim v0.8.24 h1:jP+GMeRXIR1sH1kG4lJr9ShmSjVrua5jmFZDtfYGkn4=
github.com/Microsoft/hcsshim v0.8.24/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
@@ -412,8 +412,9 @@ github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ=
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4=
github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
@@ -435,8 +436,8 @@ github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoT
github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
github.com/containerd/containerd v1.5.10 h1:3cQ2uRVCkJVcx5VombsE7105Gl9Wrl7ORAO3+4+ogf4=
github.com/containerd/containerd v1.5.10/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ=
github.com/containerd/containerd v1.5.13 h1:XqvKw9i4P7/mFrC3TSM7yV5cwFZ9avXe6M3YANKnzEE=
github.com/containerd/containerd v1.5.13/go.mod h1:3AlCrzKROjIuP3JALsY14n8YtntaUDBu7vek+rPN5Vc=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
@@ -1343,8 +1344,8 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kubevela/prism v1.4.0 h1:wYCKXA3p9YpkcSsZjGnSEGBVL+3bPoZNEt4DYs3IxW4=
github.com/kubevela/prism v1.4.0/go.mod h1:RP69+bRb57Occer6BeeF5zK3hrD1IhnYf2RNRsIdh9E=
github.com/kubevela/prism v1.4.1-0.20220613123457-94f1190f87c2 h1:TaHlO4raKI3ehVSYY8QixYMHdI0VwKHY1KPNWcUre3I=
github.com/kubevela/prism v1.4.1-0.20220613123457-94f1190f87c2/go.mod h1:RP69+bRb57Occer6BeeF5zK3hrD1IhnYf2RNRsIdh9E=
github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U=
github.com/kunwardeep/paralleltest v1.0.2/go.mod h1:ZPqNm1fVHPllh5LPVujzbVz1JN2GhLxSfY+oqUsvG30=
github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4=

View File

@@ -1058,21 +1058,22 @@ func Convert2SecName(name string) string {
// Installer helps addon enable, dependency-check, dispatch resources
type Installer struct {
ctx context.Context
config *rest.Config
addon *InstallPackage
cli client.Client
apply apply.Applicator
r *Registry
registryMeta map[string]SourceMeta
args map[string]interface{}
cache *Cache
dc *discovery.DiscoveryClient
ctx context.Context
config *rest.Config
addon *InstallPackage
cli client.Client
apply apply.Applicator
r *Registry
registryMeta map[string]SourceMeta
args map[string]interface{}
cache *Cache
dc *discovery.DiscoveryClient
skipVersionValidate bool
}
// NewAddonInstaller will create an installer for addon
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache) Installer {
return Installer{
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache, opts ...InstallOption) Installer {
i := Installer{
ctx: ctx,
config: config,
cli: cli,
@@ -1082,14 +1083,21 @@ func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *
cache: cache,
dc: discoveryClient,
}
for _, opt := range opts {
opt(&i)
}
return i
}
func (h *Installer) enableAddon(addon *InstallPackage) error {
var err error
h.addon = addon
err = checkAddonVersionMeetRequired(h.ctx, addon.SystemRequirements, h.cli, h.dc)
if err != nil {
return VersionUnMatchError{addonName: addon.Name, err: err}
if !h.skipVersionValidate {
err = checkAddonVersionMeetRequired(h.ctx, addon.SystemRequirements, h.cli, h.dc)
if err != nil {
return VersionUnMatchError{addonName: addon.Name, err: err}
}
}
if err = h.installDependency(addon); err != nil {
@@ -1445,10 +1453,23 @@ func checkSemVer(actual string, require string) (bool, error) {
}
func fetchVelaCoreImageTag(ctx context.Context, k8sClient client.Client) (string, error) {
deploy := &appsv1.Deployment{}
if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: types.KubeVelaControllerDeployment}, deploy); err != nil {
deployList := &appsv1.DeploymentList{}
if err := k8sClient.List(ctx, deployList, client.MatchingLabels{oam.LabelControllerName: oam.ApplicationControllerName}); err != nil {
return "", err
}
deploy := appsv1.Deployment{}
if len(deployList.Items) == 0 {
// backward compatible logic old version which vela-core controller has no this label
if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: types.KubeVelaControllerDeployment}, &deploy); err != nil {
if apierrors.IsNotFound(err) {
return "", errors.New("can't find a running KubeVela instance, please install it first")
}
return "", err
}
} else {
deploy = deployList.Items[0]
}
var tag string
for _, c := range deploy.Spec.Template.Spec.Containers {
if c.Name == types.DefaultKubeVelaReleaseName {

View File

@@ -196,7 +196,7 @@ var _ = Describe("Addon func test", func() {
It("fetchVelaCoreImageTag func test", func() {
deploy = appsv1.Deployment{}
tag, err := fetchVelaCoreImageTag(ctx, k8sClient)
Expect(err).Should(util.NotFoundMatcher{})
Expect(err).ShouldNot(BeNil())
Expect(tag).Should(BeEquivalentTo(""))
Expect(yaml.Unmarshal([]byte(deployYaml), &deploy)).Should(BeNil())
@@ -217,7 +217,7 @@ var _ = Describe("Addon func test", func() {
It("checkAddonVersionMeetRequired func test", func() {
deploy = appsv1.Deployment{}
Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.1"}, k8sClient, dc)).Should(util.NotFoundMatcher{})
Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.1"}, k8sClient, dc)).ShouldNot(BeNil())
Expect(yaml.Unmarshal([]byte(deployYaml), &deploy)).Should(BeNil())
deploy.SetNamespace(types.DefaultKubeVelaNS)
Expect(k8sClient.Create(ctx, &deploy)).Should(BeNil())
@@ -408,6 +408,8 @@ kind: Deployment
metadata:
name: kubevela-vela-core
namespace: vela-system
labels:
controller.oam.dev/name: vela-core
spec:
progressDeadlineSeconds: 600
replicas: 1

View File

@@ -33,6 +33,7 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/google/go-github/v32/github"
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -47,6 +48,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/oam"
version2 "github.com/oam-dev/kubevela/version"
)
@@ -791,6 +793,33 @@ func TestCheckAddonVersionMeetRequired(t *testing.T) {
MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
return nil
}),
MockList: test.NewMockListFn(nil, func(obj client.ObjectList) error {
robj := obj.(*appsv1.DeploymentList)
list := &appsv1.DeploymentList{
Items: []appsv1.Deployment{
{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
oam.LabelControllerName: oam.ApplicationControllerName,
},
},
Spec: appsv1.DeploymentSpec{
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "vela-core:v1.2.5",
},
},
},
},
},
},
},
}
list.DeepCopyInto(robj)
return nil
}),
}
ctx := context.Background()
assert.NoError(t, checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=1.2.4"}, k8sClient, nil))

BIN
pkg/addon/example-1.0.1.tgz Normal file

Binary file not shown.

View File

@@ -54,8 +54,8 @@ const (
)
// EnableAddon will enable addon with dependency check, source is where addon from.
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache) error {
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache)
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache, opts ...InstallOption) error {
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache, opts...)
pkg, err := h.loadInstallPackage(name, version)
if err != nil {
return err
@@ -93,7 +93,7 @@ func DisableAddon(ctx context.Context, cli client.Client, name string, config *r
}
// EnableAddonByLocalDir enable an addon from local dir
func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli client.Client, dc *discovery.DiscoveryClient, applicator apply.Applicator, config *rest.Config, args map[string]interface{}) error {
func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli client.Client, dc *discovery.DiscoveryClient, applicator apply.Applicator, config *rest.Config, args map[string]interface{}, opts ...InstallOption) error {
absDir, err := filepath.Abs(dir)
if err != nil {
return err
@@ -112,7 +112,7 @@ func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli cli
if err != nil {
return err
}
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil)
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil, opts...)
needEnableAddonNames, err := h.checkDependency(pkg)
if err != nil {
return err

View File

@@ -228,3 +228,11 @@ func usingAppsInfo(apps []v1beta1.Application) string {
func IsVersionRegistry(r Registry) bool {
return r.Helm != nil
}
// InstallOption define additional option for installation
type InstallOption func(installer *Installer)
// SkipValidateVersion means skip validating system version
func SkipValidateVersion(installer *Installer) {
installer.skipVersionValidate = true
}

View File

@@ -18,7 +18,6 @@ package model
import (
"fmt"
"strings"
"time"
"github.com/form3tech-oss/jwt-go"
@@ -63,17 +62,17 @@ func (u *User) ShortTableName() string {
// PrimaryKey return custom primary key
func (u *User) PrimaryKey() string {
return verifyUserValue(u.Name)
return u.Name
}
// Index return custom index
func (u *User) Index() map[string]string {
index := make(map[string]string)
if u.Name != "" {
index["name"] = verifyUserValue(u.Name)
index["name"] = u.Name
}
if u.Email != "" {
index["email"] = verifyUserValue(u.Email)
index["email"] = u.Email
}
return index
}
@@ -99,14 +98,14 @@ func (u *ProjectUser) ShortTableName() string {
// PrimaryKey return custom primary key
func (u *ProjectUser) PrimaryKey() string {
return fmt.Sprintf("%s-%s", u.ProjectName, verifyUserValue(u.Username))
return fmt.Sprintf("%s-%s", u.ProjectName, u.Username)
}
// Index return custom index
func (u *ProjectUser) Index() map[string]string {
index := make(map[string]string)
if u.Username != "" {
index["username"] = verifyUserValue(u.Username)
index["username"] = u.Username
}
if u.ProjectName != "" {
index["projectName"] = u.ProjectName
@@ -114,12 +113,6 @@ func (u *ProjectUser) Index() map[string]string {
return index
}
func verifyUserValue(v string) string {
s := strings.ReplaceAll(v, "@", "-")
s = strings.ReplaceAll(s, " ", "-")
return strings.ToLower(s)
}
// CustomClaims is the custom claims
type CustomClaims struct {
Username string `json:"username"`

View File

@@ -29,6 +29,12 @@ func init() {
RegisterModel(&WorkflowRecord{})
}
// Finished means the workflow record is finished
const Finished = "true"
// UnFinished means the workflow record is not finished
const UnFinished = "false"
// Workflow application delivery database model
type Workflow struct {
BaseModel

View File

@@ -507,14 +507,14 @@ func (c *applicationServiceImpl) UpdateApplication(ctx context.Context, app *mod
func (c *applicationServiceImpl) ListRecords(ctx context.Context, appName string) (*apisv1.ListWorkflowRecordsResponse, error) {
var record = model.WorkflowRecord{
AppPrimaryKey: appName,
Finished: "false",
Finished: model.UnFinished,
}
records, err := c.Store.List(ctx, &record, &datastore.ListOptions{})
if err != nil {
return nil, err
}
if len(records) == 0 {
record.Finished = "true"
record.Finished = model.Finished
records, err = c.Store.List(ctx, &record, &datastore.ListOptions{
Page: 1,
PageSize: 1,

View File

@@ -40,7 +40,6 @@ import (
"github.com/oam-dev/kubevela/pkg/apiserver/domain/repository"
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
"github.com/oam-dev/kubevela/pkg/apiserver/utils"
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
@@ -78,7 +77,7 @@ var _ = Describe("Test application service function", func() {
projectService = &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService}
envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService}
workflowService = &workflowServiceImpl{Store: ds, EnvService: envService}
definitionService = &definitionServiceImpl{KubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.Background())}
definitionService = &definitionServiceImpl{KubeClient: k8sClient}
envBindingService = &envBindingServiceImpl{Store: ds, EnvService: envService, WorkflowService: workflowService, KubeClient: k8sClient, DefinitionService: definitionService}
targetService = &targetServiceImpl{Store: ds, K8sClient: k8sClient}
appService = &applicationServiceImpl{

View File

@@ -60,7 +60,10 @@ var _ = Describe("Test cluster service function", func() {
secret.Name = name
secret.Namespace = prismclusterv1alpha1.StorageNamespace
secret.SetAnnotations(map[string]string{prismclusterv1alpha1.AnnotationClusterAlias: alias})
secret.SetLabels(map[string]string{clustergatewaycommon.LabelKeyClusterCredentialType: string(clustergatewayv1alpha1.CredentialTypeX509Certificate)})
secret.SetLabels(map[string]string{
clustergatewaycommon.LabelKeyClusterEndpointType: string(clustergatewayv1alpha1.ClusterEndpointTypeConst),
clustergatewaycommon.LabelKeyClusterCredentialType: string(clustergatewayv1alpha1.CredentialTypeX509Certificate),
})
time.Sleep(time.Second)
return k8sClient.Create(ctx, secret)
}

View File

@@ -21,7 +21,6 @@ import (
"encoding/json"
"fmt"
"sort"
"time"
"github.com/getkin/kin-openapi/openapi3"
"github.com/pkg/errors"
@@ -53,9 +52,11 @@ type DefinitionService interface {
UpdateDefinitionStatus(ctx context.Context, name string, status apisv1.UpdateDefinitionStatusRequest) (*apisv1.DetailDefinitionResponse, error)
}
// DefinitionHidden means the definition can not be used in VelaUX
const DefinitionHidden = "true"
type definitionServiceImpl struct {
KubeClient client.Client `inject:"kubeClient"`
caches *utils.MemoryCacheStore
}
// DefinitionQueryOption define a set of query options
@@ -80,7 +81,7 @@ const (
// NewDefinitionService new definition service
func NewDefinitionService() DefinitionService {
return &definitionServiceImpl{caches: utils.NewMemoryCacheStore(context.Background())}
return &definitionServiceImpl{}
}
func (d *definitionServiceImpl) ListDefinitions(ctx context.Context, ops DefinitionQueryOption) ([]*apisv1.DefinitionBase, error) {
@@ -95,9 +96,6 @@ func (d *definitionServiceImpl) ListDefinitions(ctx context.Context, ops Definit
}
func (d *definitionServiceImpl) listDefinitions(ctx context.Context, list *unstructured.UnstructuredList, kind string, ops DefinitionQueryOption) ([]*apisv1.DefinitionBase, error) {
if mc := d.caches.Get(ops.String()); mc != nil {
return mc.([]*apisv1.DefinitionBase), nil
}
matchLabels := metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
@@ -146,9 +144,6 @@ func (d *definitionServiceImpl) listDefinitions(ctx context.Context, list *unstr
}
defs = append(defs, definition)
}
if ops.AppliedWorkloads == "" {
d.caches.Put(ops.String(), defs, time.Minute*3)
}
return defs, nil
}
@@ -240,30 +235,27 @@ func (d *definitionServiceImpl) DetailDefinition(ctx context.Context, name, defT
if err := d.KubeClient.Get(ctx, k8stypes.NamespacedName{
Namespace: types.DefaultKubeVelaNS,
Name: fmt.Sprintf("%s-schema-%s", defType, name),
}, &cm); err != nil {
if apierrors.IsNotFound(err) {
return nil, bcode.ErrDefinitionNoSchema
}
}, &cm); err != nil && !apierrors.IsNotFound(err) {
return nil, err
}
data, ok := cm.Data[types.OpenapiV3JSONSchema]
if !ok {
return nil, bcode.ErrDefinitionNoSchema
}
schema := &openapi3.Schema{}
if err := schema.UnmarshalJSON([]byte(data)); err != nil {
return nil, err
}
// render default ui schema
defaultUISchema := renderDefaultUISchema(schema)
// patch from custom ui schema
customUISchema := d.renderCustomUISchema(ctx, name, defType, defaultUISchema)
return &apisv1.DetailDefinitionResponse{
definition := &apisv1.DetailDefinitionResponse{
DefinitionBase: *base,
APISchema: schema,
UISchema: customUISchema,
}, nil
}
data, ok := cm.Data[types.OpenapiV3JSONSchema]
if ok {
schema := &openapi3.Schema{}
if err := schema.UnmarshalJSON([]byte(data)); err != nil {
return nil, err
}
definition.APISchema = schema
// render default ui schema
defaultUISchema := renderDefaultUISchema(schema)
// patch from custom ui schema
definition.UISchema = d.renderCustomUISchema(ctx, name, defType, defaultUISchema)
}
return definition, nil
}
func (d *definitionServiceImpl) renderCustomUISchema(ctx context.Context, name, defType string, defaultSchema []*utils.UIParameter) []*utils.UIParameter {
@@ -355,7 +347,7 @@ func (d *definitionServiceImpl) UpdateDefinitionStatus(ctx context.Context, name
}
if !exist && update.HiddenInUI {
labels := def.GetLabels()
labels[types.LabelDefinitionHidden] = "true"
labels[types.LabelDefinitionHidden] = DefinitionHidden
def.SetLabels(labels)
if err := d.KubeClient.Update(ctx, def); err != nil {
return nil, err

View File

@@ -44,7 +44,7 @@ var _ = Describe("Test namespace service functions", func() {
)
BeforeEach(func() {
definitionService = &definitionServiceImpl{KubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())}
definitionService = &definitionServiceImpl{KubeClient: k8sClient}
err := k8sClient.Create(context.Background(), &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: "vela-system",
@@ -215,7 +215,6 @@ var _ = Describe("Test namespace service functions", func() {
It("Test update ui schema", func() {
du := &definitionServiceImpl{
KubeClient: k8sClient,
caches: utils.NewMemoryCacheStore(context.Background()),
}
cdata, err := ioutil.ReadFile("./testdata/workflowstep-apply-object.yaml")
Expect(err).Should(Succeed())
@@ -235,7 +234,6 @@ var _ = Describe("Test namespace service functions", func() {
It("Test update status of the definition", func() {
du := &definitionServiceImpl{
KubeClient: k8sClient,
caches: utils.NewMemoryCacheStore(context.Background()),
}
detail, err := du.UpdateDefinitionStatus(context.TODO(), "apply-object", v1.UpdateDefinitionStatusRequest{
DefinitionType: "workflowstep",

View File

@@ -27,7 +27,6 @@ import (
"github.com/oam-dev/kubevela/pkg/apiserver/domain/repository"
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
"github.com/oam-dev/kubevela/pkg/apiserver/utils"
)
var _ = Describe("Test envBindingService functions", func() {
@@ -54,7 +53,7 @@ var _ = Describe("Test envBindingService functions", func() {
projectService := &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService}
envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService}
workflowService = &workflowServiceImpl{Store: ds, KubeClient: k8sClient, EnvService: envService}
definitionService = &definitionServiceImpl{KubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())}
definitionService = &definitionServiceImpl{KubeClient: k8sClient}
envBindingService = &envBindingServiceImpl{Store: ds, WorkflowService: workflowService, DefinitionService: definitionService, KubeClient: k8sClient, EnvService: envService}
envBindingDemo1 = apisv1.EnvBinding{
Name: "envbinding-dev",

View File

@@ -0,0 +1,228 @@
/*
Copyright 2022 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package service
import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"strings"
"time"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/types"
v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
)
// NewImageService create a image service instance
func NewImageService() ImageService {
return &imageImpl{}
}
// ImageService the image service provide some handler functions about the docker image
type ImageService interface {
ListImageRepos(ctx context.Context, project string) ([]v1.ImageRegistry, error)
GetImageInfo(ctx context.Context, project, secretName, imageName string) v1.ImageInfo
}
type imageImpl struct {
K8sClient client.Client `inject:"kubeClient"`
}
// ListImageRepos list the image repositories via user configuration
func (i *imageImpl) ListImageRepos(ctx context.Context, project string) ([]v1.ImageRegistry, error) {
var secrets corev1.SecretList
if err := i.K8sClient.List(ctx, &secrets, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigType: types.ImageRegistry,
}); err != nil {
return nil, err
}
var repos []v1.ImageRegistry
for _, secret := range secrets.Items {
if secret.Labels[types.LabelConfigProject] == "" || secret.Labels[types.LabelConfigProject] == project {
repos = append(repos, v1.ImageRegistry{
Name: secret.Name,
SecretName: secret.Name,
Domain: secret.Labels[types.LabelConfigIdentifier],
})
}
}
return repos, nil
}
// GetImageInfo get the image info from image registry
func (i *imageImpl) GetImageInfo(ctx context.Context, project, secretName, imageName string) v1.ImageInfo {
var imageInfo = v1.ImageInfo{
Name: imageName,
}
ref, err := name.ParseReference(imageName)
if err != nil {
imageInfo.Message = "The image name is invalid"
return imageInfo
}
registryDomain := ref.Context().RegistryStr()
imageInfo.Registry = registryDomain
var secrets corev1.SecretList
if err := i.K8sClient.List(ctx, &secrets, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigType: types.ImageRegistry,
types.LabelConfigIdentifier: registryDomain,
}); err != nil {
log.Logger.Warnf("fail to list the docker registries, %s", err.Error())
}
var selectSecret []*corev1.Secret
var selectSecretNames []string
// get info with specified secret
if secretName != "" {
for i, secret := range secrets.Items {
if secret.Labels[types.LabelConfigProject] == "" || secret.Labels[types.LabelConfigProject] == project {
if secretName == secret.Name {
selectSecret = append(selectSecret, &secrets.Items[i])
selectSecretNames = append(selectSecretNames, secret.Name)
break
}
}
}
}
// get info with the secret which match the registry domain
if selectSecret == nil {
for i, secret := range secrets.Items {
if secret.Labels[types.LabelConfigProject] == "" || secret.Labels[types.LabelConfigProject] == project {
if secret.Labels[types.LabelConfigIdentifier] == registryDomain {
selectSecret = append(selectSecret, &secrets.Items[i])
selectSecretNames = append(selectSecretNames, secret.Name)
}
}
}
}
var username, password string
var insecure = false
var useHTTP = false
imageInfo.SecretNames = selectSecretNames
if len(selectSecret) > 0 {
insecure, useHTTP, username, password = getAccountFromSecret(*selectSecret[0], registryDomain)
}
err = getImageInfo(imageName, insecure, useHTTP, username, password, &imageInfo)
if err != nil {
imageInfo.Message = fmt.Sprintf("Fail to get the image info:%s", err.Error())
}
return imageInfo
}
// getAccountFromSecret get the username and password from the secret of `kubernetes.io/dockerconfigjson` type
// refer: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
func getAccountFromSecret(secret corev1.Secret, registryDomain string) (insecure, useHTTP bool, username, password string) {
if secret.Data != nil {
// If users use the self-signed certificate, enable the insecure-skip-verify
insecure = string(secret.Data["insecure-skip-verify"]) == "true"
useHTTP = string(secret.Data["protocol-use-http"]) == "true"
conf := secret.Data[".dockerconfigjson"]
if len(conf) > 0 {
var authConfig map[string]map[string]map[string]string
if err := json.Unmarshal(conf, &authConfig); err != nil {
log.Logger.Warnf("fail to unmarshal the secret %s , %s", secret.Name, err.Error())
return
}
if authConfig != nil && authConfig["auths"] != nil && authConfig["auths"][registryDomain] != nil {
data := authConfig["auths"][registryDomain]
username = data["username"]
password = data["password"]
}
}
}
return
}
func getImageInfo(imageName string, insecure, useHTTP bool, username, password string, info *v1.ImageInfo) error {
var options []remote.Option
if username != "" || password != "" {
basic := &authn.Basic{
Username: username,
Password: password,
}
options = append(options, remote.WithAuth(basic))
}
if insecure {
options = append(options, remote.WithTransport(&http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
// By default we wrap the transport in retries, so reduce the
// default dial timeout to 5s to avoid 5x 30s of connection
// timeouts when doing the "ping" on certain http registries.
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
// #nosec G402
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure},
}))
}
var parseOptions []name.Option
if useHTTP {
parseOptions = append(parseOptions, name.Insecure)
}
var err error
ref, err := name.ParseReference(imageName, parseOptions...)
if err != nil {
return err
}
image, err := remote.Image(ref, options...)
if err != nil {
if strings.Contains(err.Error(), "incorrect username or password") {
return fmt.Errorf("incorrect username or password")
}
var terr *transport.Error
if errors.As(err, &terr) {
fmt.Println(terr)
}
return err
}
info.Manifest, err = image.Manifest()
if err != nil {
return fmt.Errorf("fail to get the manifest:%w", err)
}
info.Info, err = image.ConfigFile()
if err != nil {
return fmt.Errorf("fail to get the config:%w", err)
}
for _, l := range info.Manifest.Layers {
info.Size += l.Size
}
info.Size += info.Manifest.Config.Size
return nil
}

View File

@@ -0,0 +1,67 @@
/*
Copyright 2022 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package service
import (
"testing"
"gotest.tools/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
velatypes "github.com/oam-dev/kubevela/apis/types"
v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
)
func TestGetImageInfo(t *testing.T) {
s2 := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "s2",
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: velatypes.ImageRegistry,
velatypes.LabelConfigProject: "",
velatypes.LabelConfigIdentifier: "index.docker.io",
},
},
Data: map[string][]byte{
"insecure-skip-verify": []byte("true"),
".dockerconfigjson": []byte(`{"auths":{"index.docker.io":{"auth":"aHlicmlkY2xvdWRAcHJvZC5YTEyMw==","username":"xxx","password":"yyy"}}}`),
},
}
insecure, useHTTP, user, pass := getAccountFromSecret(*s2, "index.docker.io")
assert.DeepEqual(t, user, "xxx")
assert.DeepEqual(t, pass, "yyy")
assert.DeepEqual(t, insecure, true)
assert.DeepEqual(t, useHTTP, false)
var cf v1.ImageInfo
// Test the public image
err := getImageInfo("nginx", false, false, "", "", &cf)
assert.DeepEqual(t, err, nil)
assert.DeepEqual(t, cf.Info.Config.Entrypoint, []string{"/docker-entrypoint.sh"})
// Test the private image
err = getImageInfo("nginx424ru823-should-not-existed", false, false, "abc", "efg", &cf)
assert.DeepEqual(t, err.Error(), "incorrect username or password")
err = getImageInfo("text.registry/test-image", false, false, "", "", &cf)
assert.DeepEqual(t, err != nil, true)
}

View File

@@ -18,15 +18,12 @@ package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/google/go-containerregistry/pkg/name"
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -41,7 +38,6 @@ import (
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
"github.com/oam-dev/kubevela/pkg/multicluster"
image "github.com/oam-dev/kubevela/pkg/utils/imageregistry"
)
// ProjectService project manage service.
@@ -59,7 +55,6 @@ type ProjectService interface {
UpdateProjectUser(ctx context.Context, projectName string, userName string, req apisv1.UpdateProjectUserRequest) (*apisv1.ProjectUserBase, error)
Init(ctx context.Context) error
GetConfigs(ctx context.Context, projectName, configType string) ([]*apisv1.Config, error)
ValidateImage(ctx context.Context, projectName, image string) (*apisv1.ImageResponse, error)
}
type projectServiceImpl struct {
@@ -623,57 +618,3 @@ func retrieveConfigFromApplication(a v1beta1.Application, project string) *apisv
Description: a.Annotations[types.AnnotationConfigDescription],
}
}
func (p *projectServiceImpl) ValidateImage(ctx context.Context, projectName, image string) (*apisv1.ImageResponse, error) {
return validateImage(ctx, p.K8sClient, projectName, image)
}
func validateImage(ctx context.Context, k8sClient client.Client, project, imageName string) (*apisv1.ImageResponse, error) {
var (
secrets v1.SecretList
username string
password string
imagePullSecret string
)
ref, err := name.ParseReference(imageName)
if err != nil {
return nil, err
}
imageURL := ref.Context().RegistryStr()
if err := k8sClient.List(ctx, &secrets, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigType: types.ImageRegistry,
types.LabelConfigIdentifier: ref.Context().RegistryStr(),
}); err != nil {
return nil, err
}
for _, s := range secrets.Items {
if s.Labels[types.LabelConfigProject] == "" || s.Labels[types.LabelConfigProject] == project {
conf := s.Data[".dockerconfigjson"]
var auths map[string]map[string]map[string]string
if err := json.Unmarshal(conf, &auths); err != nil {
return nil, err
}
imagePullSecret = s.Name
if auths["auths"] != nil && auths["auths"][imageURL] != nil {
data := auths["auths"][imageURL]
username = data["username"]
password = data["password"]
break
}
}
}
existed, err := image.IsExisted(username, password, imageName)
if err != nil {
return nil, err
}
return &apisv1.ImageResponse{
Existed: existed,
Secret: imagePullSecret,
}, nil
}

View File

@@ -18,20 +18,14 @@ package service
import (
"context"
"testing"
"time"
"github.com/google/go-cmp/cmp"
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"gotest.tools/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
@@ -299,331 +293,3 @@ var _ = Describe("Test project service functions", func() {
Expect(roles.Total).Should(BeEquivalentTo(0))
})
})
func TestProjectGetConfigs(t *testing.T) {
s := runtime.NewScheme()
v1beta1.AddToScheme(s)
corev1.AddToScheme(s)
terraformapi.AddToScheme(s)
createdTime, _ := time.Parse(time.UnixDate, "Wed Apr 7 11:06:39 PST 2022")
app1 := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "a1",
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: "terraform-provider",
"config.oam.dev/project": "p1",
},
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: common.AppStatus{Phase: common.ApplicationRunning},
}
app2 := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "a2",
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: "terraform-provider",
},
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: common.AppStatus{Phase: common.ApplicationRunning},
}
app3 := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "a3",
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: "dex-connector",
"config.oam.dev/project": "p3",
},
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: common.AppStatus{Phase: common.ApplicationRunning},
}
provider1 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "provider1",
Namespace: "default",
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: terraformapi.ProviderStatus{
State: terraformtypes.ProviderIsReady,
},
}
provider2 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "provider2",
Namespace: "default",
Labels: map[string]string{
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
},
},
Status: terraformapi.ProviderStatus{
State: terraformtypes.ProviderIsNotReady,
},
}
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(app1, app2, app3, provider1, provider2).Build()
h := &projectServiceImpl{K8sClient: k8sClient}
type args struct {
projectName string
configType string
h ProjectService
}
type want struct {
configs []*apisv1.Config
errMsg string
}
ctx := context.Background()
testcases := []struct {
name string
args args
want want
}{
{
name: "project is matched",
args: args{
projectName: "p1",
configType: "terraform-provider",
h: h,
},
want: want{
configs: []*apisv1.Config{{
ConfigType: "terraform-provider",
Name: "a1",
Project: "p1",
CreatedTime: &createdTime,
ApplicationStatus: "running",
Status: "Ready",
}, {
ConfigType: "terraform-provider",
Name: "a2",
Project: "",
CreatedTime: &createdTime,
ApplicationStatus: "running",
Status: "Ready",
}, {
Name: "provider1",
CreatedTime: &createdTime,
Status: "Ready",
}},
},
},
{
name: "project is not matched",
args: args{
projectName: "p999",
configType: "terraform-provider",
h: h,
},
want: want{
configs: []*apisv1.Config{{
ConfigType: "terraform-provider",
Name: "a2",
Project: "",
CreatedTime: &createdTime,
ApplicationStatus: "running",
Status: "Ready",
}, {
Name: "provider1",
CreatedTime: &createdTime,
Status: "Ready",
}},
},
},
{
name: "config type is empty",
args: args{
projectName: "p3",
configType: "",
h: h,
},
want: want{
configs: []*apisv1.Config{{
ConfigType: "terraform-provider",
Name: "a2",
Project: "",
CreatedTime: &createdTime,
ApplicationStatus: "running",
Status: "Ready",
}, {
ConfigType: "dex-connector",
Name: "a3",
Project: "p3",
CreatedTime: &createdTime,
ApplicationStatus: "running",
Status: "Ready",
}, {
Name: "provider1",
CreatedTime: &createdTime,
Status: "Ready",
}},
},
},
{
name: "config type is dex",
args: args{
projectName: "p3",
configType: "config-dex-connector",
h: h,
},
want: want{
configs: []*apisv1.Config{{
ConfigType: "dex-connector",
Name: "a3",
Project: "p3",
CreatedTime: &createdTime,
ApplicationStatus: "running",
Status: "Ready",
}},
},
},
{
name: "config type is invalid",
args: args{
configType: "xxx",
h: h,
},
want: want{
errMsg: "unsupported config type",
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
got, err := tc.args.h.GetConfigs(ctx, tc.args.projectName, tc.args.configType)
if tc.want.errMsg != "" || err != nil {
assert.ErrorContains(t, err, tc.want.errMsg)
}
assert.DeepEqual(t, got, tc.want.configs)
})
}
}
func TestValidateImage(t *testing.T) {
s := runtime.NewScheme()
v1beta1.AddToScheme(s)
corev1.AddToScheme(s)
terraformapi.AddToScheme(s)
s1 := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "s1",
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: velatypes.ImageRegistry,
velatypes.LabelConfigProject: "",
velatypes.LabelConfigIdentifier: "abce34289jwerojwerofaf77.com789",
},
},
Data: map[string][]byte{
".dockerconfigjson": []byte(`{"auths":{"abce34289jwerojwerofaf77.com789":{"auth":"aHlicmlkY2xvdWRAcHJvZC5YTEyMw==","username":"xxx","password":"yyy"}}}`),
},
}
k8sClient1 := fake.NewClientBuilder().WithScheme(s).WithObjects(s1).Build()
h1 := &projectServiceImpl{K8sClient: k8sClient1}
s2 := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "s2",
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: velatypes.ImageRegistry,
velatypes.LabelConfigProject: "",
velatypes.LabelConfigIdentifier: "index.docker.io",
},
},
Data: map[string][]byte{
".dockerconfigjson": []byte(`{"auths":{"index.docker.io":{"auth":"aHlicmlkY2xvdWRAcHJvZC5YTEyMw==","username":"xxx","password":"yyy"}}}`),
},
}
k8sClient2 := fake.NewClientBuilder().WithScheme(s).WithObjects(s2).Build()
h2 := &projectServiceImpl{K8sClient: k8sClient2}
type args struct {
project string
imageName string
h ProjectService
}
type want struct {
resp *apisv1.ImageResponse
errMsg string
}
ctx := context.Background()
testcases := []struct {
name string
args args
want want
}{
{
name: "validate image",
args: args{
project: "p1",
imageName: "nginx",
h: h1,
},
want: want{
resp: &apisv1.ImageResponse{
Existed: true,
},
},
},
{
name: "invalid image",
args: args{
project: "p1",
imageName: "abce34289jwerojwerofaf77.com789/d/e:v1",
h: h1,
},
want: want{
errMsg: "Get",
},
},
{
name: "private docker image",
args: args{
project: "p1",
imageName: "nginx424ru823-should-not-existed",
h: h2,
},
want: want{
errMsg: "incorrect username or password",
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
got, err := tc.args.h.ValidateImage(ctx, tc.args.project, tc.args.imageName)
if tc.want.errMsg != "" || err != nil {
assert.ErrorContains(t, err, tc.want.errMsg)
}
assert.DeepEqual(t, got, tc.want.resp)
})
}
}

View File

@@ -202,7 +202,6 @@ var ResourceMaps = map[string]resourceMetadata{
},
"applicationTemplate": {},
"config": {},
"image": {},
},
pathName: "projectName",
},

View File

@@ -50,7 +50,7 @@ func InitServiceBean(c config.Config) []interface{} {
return []interface{}{
clusterService, rbacService, projectService, envService, targetService, workflowService, oamApplicationService,
velaQLService, definitionService, addonService, envBindingService, systemInfoService, helmService, userService,
authenticationService, configService, applicationService, webhookService,
authenticationService, configService, applicationService, webhookService, NewImageService(),
}
}

View File

@@ -33,7 +33,8 @@ import (
)
const (
initAdminPassword = "VelaUX12345"
// InitAdminPassword the password of first admin user
InitAdminPassword = "VelaUX12345"
)
// UserService User manage api
@@ -70,7 +71,7 @@ func (u *userServiceImpl) Init(ctx context.Context) error {
Name: admin,
}); err != nil {
if errors.Is(err, datastore.ErrRecordNotExist) {
encrypted, err := GeneratePasswordHash(initAdminPassword)
encrypted, err := GeneratePasswordHash(InitAdminPassword)
if err != nil {
return err
}
@@ -83,7 +84,7 @@ func (u *userServiceImpl) Init(ctx context.Context) error {
return err
}
// print default password of admin user in log
log.Logger.Infof("initialized admin username and password: admin / %s", initAdminPassword)
log.Logger.Infof("initialized admin username and password: admin / %s", InitAdminPassword)
} else {
return err
}

View File

@@ -18,6 +18,8 @@ package service
import (
"context"
"encoding/base64"
"strings"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -79,5 +81,9 @@ func (v *velaQLServiceImpl) QueryView(ctx context.Context, velaQL string) (*apis
log.Logger.Errorf("decode the velaQL response to json failure %s", err.Error())
return nil, bcode.ErrParseQuery2Json
}
if strings.Contains(velaQL, "collect-logs") {
enc, _ := base64.StdEncoding.DecodeString(resp["logs"].(string))
resp["logs"] = string(enc)
}
return &resp, err
}

View File

@@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/emicklei/go-restful/v3"
@@ -426,6 +427,11 @@ func (j *jfrogHandlerImpl) handle(ctx context.Context, webhookTrigger *model.App
return nil, err
}
image := fmt.Sprintf("%s/%s:%s", jfrogReq.Data.RepoKey, jfrogReq.Data.ImageName, jfrogReq.Data.Tag)
pathArray := strings.Split(jfrogReq.Data.Path, "/")
if len(pathArray) > 2 {
image = fmt.Sprintf("%s/%s:%s", jfrogReq.Data.RepoKey, strings.Join(pathArray[:len(pathArray)-2], "/"), jfrogReq.Data.Tag)
}
if jfrogReq.Data.URL != "" {
image = fmt.Sprintf("%s/%s", jfrogReq.Data.URL, image)
}
@@ -441,7 +447,7 @@ func (j *jfrogHandlerImpl) handle(ctx context.Context, webhookTrigger *model.App
TriggerType: apisv1.TriggerTypeWebhook,
Force: true,
ImageInfo: &model.ImageInfo{
Type: model.PayloadTypeHarbor,
Type: model.PayloadTypeJFrog,
Resource: &model.ImageResource{
Digest: jfrogReq.Data.Digest,
Tag: jfrogReq.Data.Tag,

View File

@@ -75,6 +75,7 @@ func generateName(entity datastore.Entity) string {
// record the old ways here, it'll be migrated
// name := fmt.Sprintf("veladatabase-%s-%s", entity.TableName(), entity.PrimaryKey())
name := fmt.Sprintf("%s-%s", entity.ShortTableName(), entity.PrimaryKey())
name = verifyValue(name)
return strings.ReplaceAll(name, "_", "-")
}
@@ -86,6 +87,9 @@ func (m *kubeapi) generateConfigMap(entity datastore.Entity) *corev1.ConfigMap {
}
labels["table"] = entity.TableName()
labels["primaryKey"] = entity.PrimaryKey()
for k, v := range labels {
labels[k] = verifyValue(v)
}
var configMap = corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: generateName(entity),
@@ -178,6 +182,9 @@ func (m *kubeapi) Put(ctx context.Context, entity datastore.Entity) error {
}
labels["table"] = entity.TableName()
labels["primaryKey"] = entity.PrimaryKey()
for k, v := range labels {
labels[k] = verifyValue(v)
}
entity.SetUpdateTime(time.Now())
var configMap corev1.ConfigMap
if err := m.kubeClient.Get(ctx, types.NamespacedName{Namespace: m.namespace, Name: generateName(entity)}, &configMap); err != nil {
@@ -345,7 +352,7 @@ func (m *kubeapi) List(ctx context.Context, entity datastore.Entity, op *datasto
selector = selector.Add(*rq)
for k, v := range entity.Index() {
rq, err := labels.NewRequirement(k, selection.Equals, []string{v})
rq, err := labels.NewRequirement(k, selection.Equals, []string{verifyValue(v)})
if err != nil {
return nil, datastore.ErrIndexInvalid
}
@@ -353,7 +360,11 @@ func (m *kubeapi) List(ctx context.Context, entity datastore.Entity, op *datasto
}
if op != nil {
for _, inFilter := range op.In {
rq, err := labels.NewRequirement(inFilter.Key, selection.In, inFilter.Values)
var values []string
for _, value := range inFilter.Values {
values = append(values, verifyValue(value))
}
rq, err := labels.NewRequirement(inFilter.Key, selection.In, values)
if err != nil {
log.Logger.Errorf("new list requirement failure %s", err.Error())
return nil, datastore.ErrIndexInvalid
@@ -431,7 +442,7 @@ func (m *kubeapi) Count(ctx context.Context, entity datastore.Entity, filterOpti
return 0, datastore.NewDBError(err)
}
for k, v := range entity.Index() {
rq, err := labels.NewRequirement(k, selection.Equals, []string{v})
rq, err := labels.NewRequirement(k, selection.Equals, []string{verifyValue(v)})
if err != nil {
return 0, datastore.ErrIndexInvalid
}
@@ -439,7 +450,11 @@ func (m *kubeapi) Count(ctx context.Context, entity datastore.Entity, filterOpti
}
if filterOptions != nil {
for _, inFilter := range filterOptions.In {
rq, err := labels.NewRequirement(inFilter.Key, selection.In, inFilter.Values)
var values []string
for _, value := range inFilter.Values {
values = append(values, verifyValue(value))
}
rq, err := labels.NewRequirement(inFilter.Key, selection.In, values)
if err != nil {
return 0, datastore.ErrIndexInvalid
}
@@ -473,3 +488,9 @@ func (m *kubeapi) Count(ctx context.Context, entity datastore.Entity, filterOpti
}
return int64(len(items)), nil
}
func verifyValue(v string) string {
s := strings.ReplaceAll(v, "@", "-")
s = strings.ReplaceAll(s, " ", "-")
return strings.ToLower(s)
}

View File

@@ -246,4 +246,42 @@ var _ = Describe("Test kubeapi datastore driver", func() {
equal := cmp.Equal(err, datastore.ErrRecordNotExist, cmpopts.EquateErrors())
Expect(equal).Should(BeTrue())
})
It("Test verify index", func() {
var usr = model.User{Name: "can@delete", Email: "xxx@xx.com"}
err := kubeStore.Add(context.TODO(), &usr)
Expect(err).ToNot(HaveOccurred())
usr.Email = "change"
err = kubeStore.Put(context.TODO(), &usr)
Expect(err).ToNot(HaveOccurred())
err = kubeStore.Get(context.TODO(), &usr)
Expect(err).Should(BeNil())
diff := cmp.Diff(usr.Email, "change")
Expect(diff).Should(BeEmpty())
list, err := kubeStore.List(context.TODO(), &usr, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{
{
Key: "name",
Values: []string{"can@delete"},
},
}}})
Expect(err).ShouldNot(HaveOccurred())
diff = cmp.Diff(len(list), 1)
Expect(diff).Should(BeEmpty())
count, err := kubeStore.Count(context.TODO(), &usr, &datastore.FilterOptions{In: []datastore.InQueryOption{
{
Key: "name",
Values: []string{"can@delete"},
},
}})
Expect(err).ShouldNot(HaveOccurred())
Expect(count).Should(Equal(int64(1)))
usr.Name = "can@delete"
err = kubeStore.Delete(context.TODO(), &usr)
Expect(err).ShouldNot(HaveOccurred())
})
})

View File

@@ -22,6 +22,7 @@ import (
"helm.sh/helm/v3/pkg/repo"
"github.com/getkin/kin-openapi/openapi3"
registryv1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
@@ -1380,3 +1381,26 @@ type ChartRepoResponse struct {
type ChartRepoResponseList struct {
ChartRepoResponse []*ChartRepoResponse `json:"repos"`
}
// ImageInfo the docker image info
type ImageInfo struct {
Name string `json:"name"`
SecretNames []string `json:"secretNames"`
Registry string `json:"registry"`
Message string `json:"message,omitempty"`
Info *registryv1.ConfigFile `json:"info,omitempty"`
Size int64 `json:"size"`
Manifest *registryv1.Manifest `json:"manifest"`
}
// ImageRegistry the image repository info
type ImageRegistry struct {
Name string `json:"name"`
SecretName string `json:"secretName"`
Domain string `json:"domain"`
}
// ListImageRegistryResponse the response struct of listing the image registries
type ListImageRegistryResponse struct {
Registries []ImageRegistry `json:"registries"`
}

View File

@@ -76,7 +76,7 @@ func InitAPIBean() []interface{} {
RegisterAPIInterface(NewTargetAPIInterface())
RegisterAPIInterface(NewVelaQLAPIInterface())
RegisterAPIInterface(NewWebhookAPIInterface())
RegisterAPIInterface(NewHelmAPIInterface())
RegisterAPIInterface(NewRepositoryAPIInterface())
// Authentication
RegisterAPIInterface(NewAuthenticationAPIInterface())

View File

@@ -203,16 +203,6 @@ func (n *projectAPIInterface) GetWebServiceRoute() *restful.WebService {
Returns(400, "Bad Request", bcode.Bcode{}).
Writes([]*apis.Config{}))
ws.Route(ws.GET("/{projectName}/validate_image").To(n.validateImage).
Doc("validate an image in a project").
Metadata(restfulspec.KeyOpenAPITags, tags).
Filter(n.RbacService.CheckPerm("project/image", "get")).
Param(ws.QueryParameter("image", "image name").DataType("string")).
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
Returns(200, "OK", []*apis.ImageResponse{}).
Returns(400, "Bad Request", bcode.Bcode{}).
Writes([]*apis.ImageResponse{}))
ws.Filter(authCheckFilter)
return ws
}
@@ -599,23 +589,3 @@ func (n *projectAPIInterface) getConfigs(req *restful.Request, res *restful.Resp
return
}
}
func (n *projectAPIInterface) validateImage(req *restful.Request, res *restful.Response) {
resp, err := n.ProjectService.ValidateImage(req.Request.Context(), req.PathParameter("projectName"), req.QueryParameter("image"))
if err != nil {
bcode.ReturnError(req, res, err)
return
}
if resp == nil {
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
bcode.ReturnError(req, res, err)
return
}
return
}
err = res.WriteEntity(map[string]interface{}{"data": resp})
if err != nil {
bcode.ReturnError(req, res, err)
return
}
}

View File

@@ -17,7 +17,6 @@ limitations under the License.
package api
import (
"context"
"strconv"
restfulspec "github.com/emicklei/go-restful-openapi/v2"
@@ -29,16 +28,18 @@ import (
"github.com/oam-dev/kubevela/pkg/utils"
)
type helmAPIInterface struct {
HelmService service.HelmService `inject:""`
type repositoryAPIInterface struct {
HelmService service.HelmService `inject:""`
ImageService service.ImageService `inject:""`
RbacService service.RBACService `inject:""`
}
// NewHelmAPIInterface will return helm APIInterface
func NewHelmAPIInterface() Interface {
return &helmAPIInterface{}
// NewRepositoryAPIInterface will return the repository APIInterface
func NewRepositoryAPIInterface() Interface {
return &repositoryAPIInterface{}
}
func (h helmAPIInterface) GetWebServiceRoute() *restful.WebService {
func (h repositoryAPIInterface) GetWebServiceRoute() *restful.WebService {
ws := new(restful.WebService)
ws.Path(versionPrefix+"/repository").
Consumes(restful.MIME_XML, restful.MIME_JSON).
@@ -47,11 +48,12 @@ func (h helmAPIInterface) GetWebServiceRoute() *restful.WebService {
tags := []string{"repository", "helm"}
// List charts
// List chart repos
ws.Route(ws.GET("/chart_repos").To(h.listRepo).
Doc("list chart repo").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(ws.QueryParameter("project", "the config project").DataType("string")).
Param(ws.QueryParameter("project", "the config project").DataType("string").Required(true)).
Filter(h.RbacService.CheckPerm("project/config", "list")).
Returns(200, "OK", []string{}).
Returns(400, "Bad Request", bcode.Bcode{}).
Writes([]string{}))
@@ -86,11 +88,31 @@ func (h helmAPIInterface) GetWebServiceRoute() *restful.WebService {
Returns(400, "Bad Request", bcode.Bcode{}).
Writes([]string{}))
ws.Route(ws.GET("/image/repos").To(h.getImageRepos).
Doc("get the oci repos").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(ws.QueryParameter("project", "the config project").DataType("string").Required(true)).
Filter(h.RbacService.CheckPerm("project/config", "list")).
Returns(200, "OK", v1.ListImageRegistryResponse{}).
Returns(400, "Bad Request", bcode.Bcode{}).
Writes([]string{}))
ws.Route(ws.GET("/image/info").To(h.getImageInfo).
Doc("get the oci repos").
Metadata(restfulspec.KeyOpenAPITags, tags).
Param(ws.QueryParameter("project", "the config project").DataType("string").Required(true)).
Param(ws.QueryParameter("name", "the image name").DataType("string").Required(true)).
Param(ws.QueryParameter("secretName", "the secret name of the image repository").DataType("string")).
Filter(h.RbacService.CheckPerm("project/config", "list")).
Returns(200, "OK", v1.ImageInfo{}).
Returns(400, "Bad Request", bcode.Bcode{}).
Writes([]string{}))
ws.Filter(authCheckFilter)
return ws
}
func (h helmAPIInterface) listCharts(req *restful.Request, res *restful.Response) {
func (h repositoryAPIInterface) listCharts(req *restful.Request, res *restful.Response) {
url := utils.Sanitize(req.QueryParameter("repoUrl"))
secName := utils.Sanitize(req.QueryParameter("secretName"))
skipCache, err := isSkipCache(req)
@@ -98,7 +120,7 @@ func (h helmAPIInterface) listCharts(req *restful.Request, res *restful.Response
bcode.ReturnError(req, res, bcode.ErrSkipCacheParameter)
return
}
charts, err := h.HelmService.ListChartNames(context.Background(), url, secName, skipCache)
charts, err := h.HelmService.ListChartNames(req.Request.Context(), url, secName, skipCache)
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -110,7 +132,7 @@ func (h helmAPIInterface) listCharts(req *restful.Request, res *restful.Response
}
}
func (h helmAPIInterface) listVersions(req *restful.Request, res *restful.Response) {
func (h repositoryAPIInterface) listVersions(req *restful.Request, res *restful.Response) {
url := req.QueryParameter("repoUrl")
chartName := req.PathParameter("chart")
secName := req.QueryParameter("secretName")
@@ -120,7 +142,7 @@ func (h helmAPIInterface) listVersions(req *restful.Request, res *restful.Respon
return
}
versions, err := h.HelmService.ListChartVersions(context.Background(), url, chartName, secName, skipCache)
versions, err := h.HelmService.ListChartVersions(req.Request.Context(), url, chartName, secName, skipCache)
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -132,7 +154,7 @@ func (h helmAPIInterface) listVersions(req *restful.Request, res *restful.Respon
}
}
func (h helmAPIInterface) chartValues(req *restful.Request, res *restful.Response) {
func (h repositoryAPIInterface) chartValues(req *restful.Request, res *restful.Response) {
url := req.QueryParameter("repoUrl")
secName := req.QueryParameter("secretName")
chartName := req.PathParameter("chart")
@@ -143,7 +165,7 @@ func (h helmAPIInterface) chartValues(req *restful.Request, res *restful.Respons
return
}
versions, err := h.HelmService.GetChartValues(context.Background(), url, chartName, version, secName, skipCache)
versions, err := h.HelmService.GetChartValues(req.Request.Context(), url, chartName, version, secName, skipCache)
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -155,9 +177,9 @@ func (h helmAPIInterface) chartValues(req *restful.Request, res *restful.Respons
}
}
func (h helmAPIInterface) listRepo(req *restful.Request, res *restful.Response) {
func (h repositoryAPIInterface) listRepo(req *restful.Request, res *restful.Response) {
project := req.QueryParameter("project")
repos, err := h.HelmService.ListChartRepo(context.Background(), project)
repos, err := h.HelmService.ListChartRepo(req.Request.Context(), project)
if err != nil {
bcode.ReturnError(req, res, err)
return
@@ -169,6 +191,31 @@ func (h helmAPIInterface) listRepo(req *restful.Request, res *restful.Response)
}
}
func (h repositoryAPIInterface) getImageRepos(req *restful.Request, res *restful.Response) {
project := req.QueryParameter("project")
repos, err := h.ImageService.ListImageRepos(req.Request.Context(), project)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
err = res.WriteEntity(v1.ListImageRegistryResponse{Registries: repos})
if err != nil {
bcode.ReturnError(req, res, err)
return
}
}
func (h repositoryAPIInterface) getImageInfo(req *restful.Request, res *restful.Response) {
project := req.QueryParameter("project")
imageInfo := h.ImageService.GetImageInfo(req.Request.Context(), project, req.QueryParameter("secretName"), req.QueryParameter("name"))
err := res.WriteEntity(imageInfo)
if err != nil {
bcode.ReturnError(req, res, err)
return
}
}
func isSkipCache(req *restful.Request) (bool, error) {
skipStr := req.QueryParameter("skipCache")
skipCache := false

View File

@@ -47,6 +47,11 @@ func ContextWithUserInfo(ctx context.Context, app *v1beta1.Application) context.
return request.WithUser(ctx, GetUserInfoInAnnotation(&app.ObjectMeta))
}
// ContextClearUserInfo clear user info in context
func ContextClearUserInfo(ctx context.Context) context.Context {
return request.WithUser(ctx, nil)
}
// SetUserInfoInAnnotation set username and group from userInfo into annotations
// it will clear the existing service account annotation in avoid of permission leak
func SetUserInfoInAnnotation(obj *metav1.ObjectMeta, userInfo authv1.UserInfo) {

View File

@@ -51,7 +51,7 @@ func (c *HTTPCmd) Run(meta *registry.Meta) (res interface{}, err error) {
var (
r io.Reader
client = &http.Client{
Transport: &http.Transport{},
Transport: http.DefaultTransport,
Timeout: time.Second * 3,
}
)

View File

@@ -99,3 +99,40 @@ func NewDefaultFactory(cfg *rest.Config) Factory {
copiedCfg.Wrap(multicluster.NewSecretModeMultiClusterRoundTripper)
return &defaultFactory{cfg: &copiedCfg}
}
type deferredFactory struct {
sync.Mutex
Factory
ConfigGetter
}
// NewDeferredFactory create a factory that will only get KubeConfig until it is needed for the first time
func NewDeferredFactory(getter ConfigGetter) Factory {
return &deferredFactory{ConfigGetter: getter}
}
func (f *deferredFactory) init() {
cfg, err := f.ConfigGetter()
cmdutil.CheckErr(err)
f.Factory = NewDefaultFactory(cfg)
}
// Config return the kubeConfig
func (f *deferredFactory) Config() *rest.Config {
f.Lock()
defer f.Unlock()
if f.Factory == nil {
f.init()
}
return f.Factory.Config()
}
// Client return the kubeClient
func (f *deferredFactory) Client() client.Client {
f.Lock()
defer f.Unlock()
if f.Factory == nil {
f.init()
}
return f.Factory.Client()
}

View File

@@ -264,6 +264,10 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
var traitStatusList []common.ApplicationTraitStatus
for _, tr := range wl.Traits {
if tr.FullTemplate.TraitDefinition.Spec.ControlPlaneOnly {
namespace = appRev.GetNamespace()
wl.Ctx.SetCtx(context.WithValue(wl.Ctx.GetCtx(), multicluster.ClusterContextKey, multicluster.ClusterLocalName))
}
var traitStatus = common.ApplicationTraitStatus{
Type: tr.Name,
Healthy: true,
@@ -277,6 +281,8 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, evaluate status message error", appName, wl.Name, tr.Name)
}
traitStatusList = append(traitStatusList, traitStatus)
namespace = appRev.GetNamespace()
wl.Ctx.SetCtx(context.WithValue(wl.Ctx.GetCtx(), multicluster.ClusterContextKey, status.Cluster))
}
status.Traits = traitStatusList
@@ -299,12 +305,12 @@ func setStatus(status *common.ApplicationComponentStatus, observedGeneration, ge
}
return true
}
status.Message = message
if !isLatest() || state != terraformtypes.Available {
status.Healthy = false
return false
}
status.Healthy = true
status.Message = message
return true
}

View File

@@ -188,7 +188,7 @@ func convertStepProperties(step *v1beta1.WorkflowStep, app *v1beta1.Application)
}
func checkDependsOnValidComponent(dependsOnComponentNames, allComponentNames []string) (string, bool) {
// does not depends on other components
// does not depend on other components
if dependsOnComponentNames == nil {
return "", true
}

View File

@@ -30,7 +30,7 @@ import (
"cuelang.org/go/cue/build"
"github.com/getkin/kin-openapi/openapi3"
"github.com/pkg/errors"
git "gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4"
v1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -215,25 +215,26 @@ func GetOpenAPISchemaFromTerraformComponentDefinition(configuration string) ([]b
// GetTerraformConfigurationFromRemote gets Terraform Configuration(HCL)
func GetTerraformConfigurationFromRemote(name, remoteURL, remotePath string) (string, error) {
tmpPath := filepath.Join("./tmp/terraform", name)
// Check if the directory exists. If yes, remove it.
if _, err := os.Stat(tmpPath); err == nil {
err := os.RemoveAll(tmpPath)
if err != nil {
return "", errors.Wrap(err, "failed to remove the directory")
}
}
_, err := git.PlainClone(tmpPath, false, &git.CloneOptions{
URL: remoteURL,
Progress: nil,
})
userHome, err := os.UserHomeDir()
if err != nil {
return "", err
}
cachePath := filepath.Join(userHome, ".vela", "terraform", name)
// Check if the directory exists. If yes, remove it.
entities, err := os.ReadDir(cachePath)
if err != nil || len(entities) == 0 {
fmt.Printf("loading terraform module %s into %s from %s\n", name, cachePath, remoteURL)
if _, err = git.PlainClone(cachePath, false, &git.CloneOptions{
URL: remoteURL,
Progress: os.Stdout,
}); err != nil {
return "", err
}
}
tfPath := filepath.Join(tmpPath, remotePath, "variables.tf")
tfPath := filepath.Join(cachePath, remotePath, "variables.tf")
if _, err := os.Stat(tfPath); err != nil {
tfPath = filepath.Join(tmpPath, remotePath, "main.tf")
tfPath = filepath.Join(cachePath, remotePath, "main.tf")
if _, err := os.Stat(tfPath); err != nil {
return "", errors.Wrap(err, "failed to find main.tf or variables.tf in Terraform configurations of the remote repository")
}
@@ -242,10 +243,6 @@ func GetTerraformConfigurationFromRemote(name, remoteURL, remotePath string) (st
if err != nil {
return "", errors.Wrap(err, "failed to read Terraform configuration")
}
if err := os.RemoveAll(tmpPath); err != nil {
return "", err
}
return string(conf), nil
}

View File

@@ -17,24 +17,16 @@
package utils
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
. "github.com/agiledragon/gomonkey/v2"
"github.com/pkg/errors"
"gopkg.in/src-d/go-git.v4"
"gotest.tools/assert"
)
func TestGetTerraformConfigurationFromRemote(t *testing.T) {
// If you hit a panic on macOS as below, please fix it by referencing https://github.com/eisenxp/macos-golink-wrapper.
// panic: permission denied [recovered]
// panic: permission denied
type want struct {
config string
errMsg string
@@ -46,8 +38,6 @@ func TestGetTerraformConfigurationFromRemote(t *testing.T) {
path string
data []byte
variableFile string
// mockWorkingPath will create `/tmp/terraform`
mockWorkingPath bool
}
cases := map[string]struct {
args args
@@ -57,7 +47,7 @@ func TestGetTerraformConfigurationFromRemote(t *testing.T) {
args: args{
name: "valid",
url: "https://github.com/kubevela-contrib/terraform-modules.git",
path: "",
path: "unittest/",
data: []byte(`
variable "aaa" {
type = list(object({
@@ -85,7 +75,7 @@ variable "aaa" {
args: args{
name: "aws-subnet",
url: "https://github.com/kubevela-contrib/terraform-modules.git",
path: "aws/subnet",
path: "unittest/aws/subnet",
data: []byte(`
variable "aaa" {
type = list(object({
@@ -109,47 +99,20 @@ variable "aaa" {
}`,
},
},
"working path exists": {
args: args{
variableFile: "main.tf",
mockWorkingPath: true,
},
want: want{
errMsg: "failed to remove the directory",
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
if tc.args.mockWorkingPath {
err := os.MkdirAll("./tmp/terraform", 0755)
assert.NilError(t, err)
defer os.RemoveAll("./tmp/terraform")
patch1 := ApplyFunc(os.Remove, func(_ string) error {
return errors.New("failed")
})
defer patch1.Reset()
patch2 := ApplyFunc(os.Open, func(_ string) (*os.File, error) {
return nil, errors.New("failed")
})
defer patch2.Reset()
}
patch := ApplyFunc(git.PlainCloneContext, func(ctx context.Context, path string, isBare bool, o *git.CloneOptions) (*git.Repository, error) {
var tmpPath string
if tc.args.path != "" {
tmpPath = filepath.Join("./tmp/terraform", tc.args.name, tc.args.path)
} else {
tmpPath = filepath.Join("./tmp/terraform", tc.args.name)
}
home, _ := os.UserHomeDir()
path := filepath.Join(home, ".vela", "terraform")
tmpPath := filepath.Join(path, tc.args.name, tc.args.path)
if len(tc.args.data) > 0 {
err := os.MkdirAll(tmpPath, os.ModePerm)
assert.NilError(t, err)
err = ioutil.WriteFile(filepath.Clean(filepath.Join(tmpPath, tc.args.variableFile)), tc.args.data, 0644)
assert.NilError(t, err)
return nil, nil
})
defer patch.Reset()
}
defer os.RemoveAll(tmpPath)
conf, err := GetTerraformConfigurationFromRemote(tc.args.name, tc.args.url, tc.args.path)
if tc.want.errMsg != "" {

View File

@@ -51,7 +51,7 @@ const (
ContextComponents = "components"
// ContextComponentType is the component type of current trait binding with
ContextComponentType = "componentType"
// ComponentRevisionPlaceHolder is the component revision name placeHolder, this field will be replace with real value
// ComponentRevisionPlaceHolder is the component revision name placeHolder, this field will be replaced with real value
// after component be created
ComponentRevisionPlaceHolder = "KUBEVELA_COMPONENT_REVISION_PLACEHOLDER"
// ContextDataArtifacts is used to store unstructured resources of components

View File

@@ -302,7 +302,7 @@ func strategyUnify(baseFile *ast.File, patchFile *ast.File, params *UnifyParams,
ret := baseInst.Value().Unify(patchInst.Value())
rv, err := toString(ret)
rv, err := toString(ret, removeTmpVar)
if err != nil {
return rv, errors.WithMessage(err, " format result toString")
}
@@ -358,7 +358,11 @@ func jsonMergePatch(base cue.Value, patch cue.Value) (string, error) {
if err != nil {
return "", errors.Wrapf(err, "failed to merge base value and patch value by JsonMergePatch")
}
return string(merged), nil
output, err := OpenBaiscLit(string(merged))
if err != nil {
return "", errors.Wrapf(err, "failed to parse open basic lit for merged result")
}
return output, nil
}
func jsonPatch(base cue.Value, patch cue.Value) (string, error) {
@@ -379,5 +383,9 @@ func jsonPatch(base cue.Value, patch cue.Value) (string, error) {
if err != nil {
return "", errors.Wrapf(err, "failed to apply json patch")
}
return string(merged), nil
output, err := OpenBaiscLit(string(merged))
if err != nil {
return "", errors.Wrapf(err, "failed to parse open basic lit for merged result")
}
return output, nil
}

View File

@@ -358,3 +358,38 @@ func listOpen(expr ast.Node) {
}
}
}
func removeTmpVar(expr ast.Node) ast.Node {
switch v := expr.(type) {
case *ast.File:
for _, decl := range v.Decls {
removeTmpVar(decl)
}
case *ast.Field:
removeTmpVar(v.Value)
case *ast.StructLit:
var elts []ast.Decl
for _, elt := range v.Elts {
if field, isField := elt.(*ast.Field); isField {
if ident, isIdent := field.Label.(*ast.Ident); isIdent && strings.HasPrefix(ident.Name, "_") {
continue
}
}
removeTmpVar(elt)
elts = append(elts, elt)
}
v.Elts = elts
case *ast.BinaryExpr:
removeTmpVar(v.X)
removeTmpVar(v.Y)
case *ast.EmbedDecl:
removeTmpVar(v.Expr)
case *ast.Comprehension:
removeTmpVar(v.Value)
case *ast.ListLit:
for _, elt := range v.Elts {
removeTmpVar(elt)
}
}
return expr
}

View File

@@ -23,6 +23,7 @@ import (
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/parser"
"github.com/bmizerany/assert"
"github.com/stretchr/testify/require"
)
func TestWalk(t *testing.T) {
@@ -128,3 +129,33 @@ func TestWalk(t *testing.T) {
}
}
func TestRemoveTmpVar(t *testing.T) {
src := `spec: {
_tmp: "x"
list: [{
_tmp: "x"
retain: "y"
}, {
_tmp: "x"
retain: "z"
}]
retain: "y"
}
`
r := require.New(t)
var runtime cue.Runtime
inst, err := runtime.Compile("-", src)
r.NoError(err)
s, err := toString(inst.Value(), removeTmpVar)
r.NoError(err)
r.Equal(`spec: {
list: [{
retain: "y"
}, {
retain: "z"
}]
retain: "y"
}
`, s)
}

View File

@@ -302,9 +302,6 @@ func (ctx *templateContext) GetCtx() context.Context {
}
func (ctx *templateContext) SetCtx(newContext context.Context) {
if ctx.ctx != nil {
return
}
ctx.ctx = newContext
}

View File

@@ -22,6 +22,9 @@ import (
"fmt"
"github.com/briandowns/spinner"
prismclusterv1alpha1 "github.com/kubevela/prism/pkg/apis/cluster/v1alpha1"
clusterv1alpha1 "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
clustercommon "github.com/oam-dev/cluster-gateway/pkg/common"
"github.com/oam-dev/cluster-register/pkg/hub"
"github.com/oam-dev/cluster-register/pkg/spoke"
"github.com/pkg/errors"
@@ -36,11 +39,7 @@ import (
ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
clusterv1alpha1 "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
clustercommon "github.com/oam-dev/cluster-gateway/pkg/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/policy/envbinding"
"github.com/oam-dev/kubevela/pkg/utils"
velaerrors "github.com/oam-dev/kubevela/pkg/utils/errors"
@@ -49,6 +48,7 @@ import (
// KubeClusterConfig info for cluster management
type KubeClusterConfig struct {
FilePath string
ClusterName string
CreateNamespace string
*clientcmdapi.Config
@@ -84,15 +84,26 @@ func (clusterConfig *KubeClusterConfig) Validate() error {
return nil
}
// RegisterByVelaSecret create cluster secrets for KubeVela to use
func (clusterConfig *KubeClusterConfig) RegisterByVelaSecret(ctx context.Context, cli client.Client) error {
if err := ensureClusterNotExists(ctx, cli, clusterConfig.ClusterName); err != nil {
return errors.Wrapf(err, "cannot use cluster name %s", clusterConfig.ClusterName)
// PostRegistration try to create namespace after cluster registered. If failed, cluster will be unregistered.
func (clusterConfig *KubeClusterConfig) PostRegistration(ctx context.Context, cli client.Client) error {
if clusterConfig.CreateNamespace == "" {
return nil
}
if err := ensureNamespaceExists(ctx, cli, clusterConfig.ClusterName, clusterConfig.CreateNamespace); err != nil {
_ = DetachCluster(ctx, cli, clusterConfig.ClusterName, DetachClusterManagedClusterKubeConfigPathOption(clusterConfig.FilePath))
return fmt.Errorf("failed to ensure %s namespace installed in cluster %s: %w", clusterConfig.CreateNamespace, clusterConfig.ClusterName, err)
}
return nil
}
func (clusterConfig *KubeClusterConfig) createClusterSecret(ctx context.Context, cli client.Client, withEndpoint bool) error {
var credentialType clusterv1alpha1.CredentialType
data := map[string][]byte{
"endpoint": []byte(clusterConfig.Cluster.Server),
"ca.crt": clusterConfig.Cluster.CertificateAuthorityData,
data := map[string][]byte{}
if withEndpoint {
data["endpoint"] = []byte(clusterConfig.Cluster.Server)
if !clusterConfig.Cluster.InsecureSkipTLSVerify {
data["ca.crt"] = clusterConfig.Cluster.CertificateAuthorityData
}
}
if len(clusterConfig.AuthInfo.Token) > 0 {
credentialType = clusterv1alpha1.CredentialTypeServiceAccountToken
@@ -113,22 +124,50 @@ func (clusterConfig *KubeClusterConfig) RegisterByVelaSecret(ctx context.Context
Type: corev1.SecretTypeOpaque,
Data: data,
}
if err := cli.Create(ctx, secret); err != nil {
return cli.Create(ctx, secret)
}
// RegisterByVelaSecret create cluster secrets for KubeVela to use
func (clusterConfig *KubeClusterConfig) RegisterByVelaSecret(ctx context.Context, cli client.Client) error {
if err := ensureClusterNotExists(ctx, cli, clusterConfig.ClusterName); err != nil {
return errors.Wrapf(err, "cannot use cluster name %s", clusterConfig.ClusterName)
}
if err := clusterConfig.createClusterSecret(ctx, cli, true); err != nil {
return errors.Wrapf(err, "failed to add cluster to kubernetes")
}
// TODO(somefive): create namespace now only work for cluster secret
if clusterConfig.CreateNamespace != "" {
if err := ensureNamespaceExists(ctx, cli, clusterConfig.ClusterName, clusterConfig.CreateNamespace); err != nil {
_ = cli.Delete(ctx, secret)
return errors.Wrapf(err, "failed to ensure %s namespace installed in cluster %s", clusterConfig.CreateNamespace, clusterConfig.ClusterName)
return clusterConfig.PostRegistration(ctx, cli)
}
// CreateBootstrapConfigMapIfNotExists alternative to
// https://github.com/kubernetes/kubernetes/blob/v1.24.1/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo/clusterinfo.go#L43
func CreateBootstrapConfigMapIfNotExists(ctx context.Context, cli client.Client) error {
cm := &corev1.ConfigMap{}
key := apitypes.NamespacedName{Namespace: metav1.NamespacePublic, Name: "cluster-info"}
if err := cli.Get(ctx, key, cm); err != nil {
if apierrors.IsNotFound(err) {
cm.ObjectMeta = metav1.ObjectMeta{Namespace: key.Namespace, Name: key.Name}
adminConfig, err := clientcmd.NewDefaultPathOptions().GetStartingConfig()
if err != nil {
return err
}
adminCluster := adminConfig.Contexts[adminConfig.CurrentContext].Cluster
bs, err := clientcmd.Write(clientcmdapi.Config{
Clusters: map[string]*clientcmdapi.Cluster{"": adminConfig.Clusters[adminCluster]},
})
if err != nil {
return err
}
cm.Data = map[string]string{"kubeconfig": string(bs)}
return cli.Create(ctx, cm)
}
return err
}
return nil
}
// RegisterClusterManagedByOCM create ocm managed cluster for use
// TODO(somefive): OCM ManagedCluster only support cli join now
func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.Context, args *JoinClusterArgs) error {
func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.Context, cli client.Client, args *JoinClusterArgs) error {
newTrackingSpinner := args.trackingSpinnerFactory
hubCluster, err := hub.NewHubCluster(args.hubConfig)
if err != nil {
@@ -162,6 +201,9 @@ func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.
return errors.Wrap(err, "fail to convert spoke-cluster kubeconfig")
}
if err = CreateBootstrapConfigMapIfNotExists(ctx, cli); err != nil {
return fmt.Errorf("failed to ensure cluster-info ConfigMap in kube-public namespace exists: %w", err)
}
spokeTracker := newTrackingSpinner("Building registration config for the managed cluster")
spokeTracker.FinalMSG = "Successfully prepared registration config.\n"
spokeTracker.Start()
@@ -170,6 +212,7 @@ func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.
args.ioStreams.Infof("Using the api endpoint from hub kubeconfig %q as registration entry.\n", args.hubConfig.Host)
overridingRegistrationEndpoint = args.hubConfig.Host
}
hubKubeToken, err := hubCluster.GenerateHubClusterKubeConfig(ctx, overridingRegistrationEndpoint)
if err != nil {
return errors.Wrap(err, "fail to generate the token for spoke-cluster")
@@ -228,7 +271,7 @@ func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.
// LoadKubeClusterConfigFromFile create KubeClusterConfig from kubeconfig file
func LoadKubeClusterConfigFromFile(filepath string) (*KubeClusterConfig, error) {
clusterConfig := &KubeClusterConfig{}
clusterConfig := &KubeClusterConfig{FilePath: filepath}
var err error
clusterConfig.Config, err = clientcmd.LoadFromFile(filepath)
if err != nil {
@@ -343,7 +386,7 @@ func JoinClusterByKubeConfig(ctx context.Context, cli client.Client, kubeconfigP
return nil, errors.Wrapf(err, "failed to determine the registration endpoint for the hub cluster "+
"when parsing --in-cluster-bootstrap flag")
}
if err = clusterConfig.RegisterClusterManagedByOCM(ctx, args); err != nil {
if err = clusterConfig.RegisterClusterManagedByOCM(ctx, cli, args); err != nil {
return clusterConfig, err
}
}
@@ -382,12 +425,12 @@ func DetachCluster(ctx context.Context, cli client.Client, clusterName string, o
if clusterName == ClusterLocalName {
return ErrReservedLocalClusterName
}
vc, err := GetVirtualCluster(ctx, cli, clusterName)
vc, err := prismclusterv1alpha1.NewClusterClient(cli).Get(ctx, clusterName)
if err != nil {
return err
}
switch vc.Type {
switch vc.Spec.CredentialType {
case clusterv1alpha1.CredentialTypeX509Certificate, clusterv1alpha1.CredentialTypeServiceAccountToken:
clusterSecret, err := getMutableClusterSecret(ctx, cli, clusterName)
if err != nil {
@@ -396,7 +439,7 @@ func DetachCluster(ctx context.Context, cli client.Client, clusterName string, o
if err := cli.Delete(ctx, clusterSecret); err != nil {
return errors.Wrapf(err, "failed to detach cluster %s", clusterName)
}
case types.CredentialTypeOCMManagedCluster:
case prismclusterv1alpha1.CredentialTypeOCMManagedCluster:
if args.managedClusterKubeConfigPath == "" {
return errors.New("kubeconfig-path must be set to detach ocm managed cluster")
}
@@ -414,7 +457,7 @@ func DetachCluster(ctx context.Context, cli client.Client, clusterName string, o
return err
}
managedCluster := ocmclusterv1.ManagedCluster{ObjectMeta: metav1.ObjectMeta{Name: clusterName}}
if err = cli.Delete(context.Background(), &managedCluster); err != nil {
if err = cli.Delete(ctx, &managedCluster); err != nil {
if !apierrors.IsNotFound(err) {
return err
}

View File

@@ -98,6 +98,9 @@ const (
LabelProject = "core.oam.dev/project"
LabelResourceRules = "rules.oam.dev/resources"
// LabelControllerName indicates the controller name
LabelControllerName = "controller.oam.dev/name"
)
const (

View File

@@ -16,7 +16,10 @@ limitations under the License.
package oam
// SystemDefinitonNamespace golbal value for controller and webhook systemlevel namespace
var (
// SystemDefinitonNamespace golbal value for controller and webhook systemlevel namespace
SystemDefinitonNamespace string = "vela-system"
// ApplicationControllerName means the controller is application
ApplicationControllerName string = "vela-core"
)

View File

@@ -49,6 +49,7 @@ func newDeleteConfig(options ...DeleteOption) *deleteConfig {
// Delete delete resources
func (h *resourceKeeper) Delete(ctx context.Context, manifests []*unstructured.Unstructured, options ...DeleteOption) (err error) {
h.ClearNamespaceForClusterScopedResources(manifests)
if err = h.AdmissionCheck(ctx, manifests); err != nil {
return err
}

View File

@@ -59,6 +59,7 @@ func (h *resourceKeeper) Dispatch(ctx context.Context, manifests []*unstructured
if h.applyOncePolicy != nil && h.applyOncePolicy.Enable && h.applyOncePolicy.Rules == nil {
options = append(options, MetaOnlyOption{})
}
h.ClearNamespaceForClusterScopedResources(manifests)
// 0. check admission
if err = h.AdmissionCheck(ctx, manifests); err != nil {
return err
@@ -104,6 +105,7 @@ func (h *resourceKeeper) record(ctx context.Context, manifests []*unstructured.U
}
cfg := newDispatchConfig(options...)
ctx = auth.ContextClearUserInfo(ctx)
if len(rootManifests) != 0 {
rt, err := h.getRootRT(ctx)
if err != nil {

View File

@@ -0,0 +1,35 @@
/*
Copyright 2022 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resourcekeeper
import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// ClearNamespaceForClusterScopedResources clear namespace for cluster scoped resources
func (h *resourceKeeper) ClearNamespaceForClusterScopedResources(manifests []*unstructured.Unstructured) {
for _, manifest := range manifests {
mappings, err := h.Client.RESTMapper().RESTMappings(manifest.GroupVersionKind().GroupKind(), manifest.GroupVersionKind().Version)
if err != nil {
continue
}
if len(mappings) > 0 && mappings[0].Scope.Name() == meta.RESTScopeNameRoot {
manifest.SetNamespace("")
}
}
}

View File

@@ -0,0 +1,55 @@
/*
Copyright 2022 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resourcekeeper
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/utils/apply"
)
var _ = Describe("Test ResourceKeeper utilities", func() {
It("Test ClearNamespaceForClusterScopedResources", func() {
cli := testClient
h := &resourceKeeper{
Client: cli,
app: &v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "default"}},
applicator: apply.NewAPIApplicator(cli),
cache: newResourceCache(cli),
}
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "example", Namespace: "vela"}}
nsObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns)
Expect(err).Should(Succeed())
cm := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "example", Namespace: "vela"}}
cmObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(cm)
Expect(err).Should(Succeed())
uns := []*unstructured.Unstructured{{Object: nsObj}, {Object: cmObj}}
uns[0].SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Namespace"))
uns[1].SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("ConfigMap"))
h.ClearNamespaceForClusterScopedResources(uns)
Expect(uns[0].GetNamespace()).Should(Equal(""))
Expect(uns[1].GetNamespace()).Should(Equal("vela"))
})
})

View File

@@ -85,10 +85,7 @@ func (options *ResourceTreePrintOptions) loadResourceRows(currentRT *v1beta1.Res
if mr.Deleted {
continue
}
rows = append(rows, &resourceRow{
mr: mr.DeepCopy(),
status: resourceRowStatusUpdated,
})
rows = append(rows, buildResourceRow(mr, resourceRowStatusUpdated))
}
}
for _, rt := range historyRT {
@@ -100,10 +97,7 @@ func (options *ResourceTreePrintOptions) loadResourceRows(currentRT *v1beta1.Res
}
}
if matchedRow == nil {
rows = append(rows, &resourceRow{
mr: mr.DeepCopy(),
status: resourceRowStatusOutdated,
})
rows = append(rows, buildResourceRow(mr, resourceRowStatusOutdated))
}
}
}
@@ -410,3 +404,14 @@ func RetrieveKubeCtlGetMessageGenerator(cfg *rest.Config) (ResourceDetailRetriev
return nil
}, nil
}
func buildResourceRow(mr v1beta1.ManagedResource, resourceStatus string) *resourceRow {
rr := &resourceRow{
mr: mr.DeepCopy(),
status: resourceStatus,
}
if rr.mr.Cluster == "" {
rr.mr.Cluster = multicluster.ClusterLocalName
}
return rr
}

View File

@@ -22,6 +22,10 @@ import (
"github.com/stretchr/testify/require"
"k8s.io/utils/pointer"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/multicluster"
)
func TestResourceTreePrintOption_getWidthForDetails(t *testing.T) {
@@ -46,3 +50,39 @@ func TestResourceTreePrintOptions_wrapDetails(t *testing.T) {
},
options._wrapDetails(detail, 40))
}
func TestBuildResourceRow(t *testing.T) {
r := require.New(t)
cases := map[string]struct {
Cluster string
ResourceRowStatus string
ExpectedCluster string
ExpectedResourceRowStatus string
}{
"localCluster": {
Cluster: "",
ResourceRowStatus: resourceRowStatusUpdated,
ExpectedCluster: multicluster.ClusterLocalName,
ExpectedResourceRowStatus: resourceRowStatusUpdated,
},
"remoteCluster": {
Cluster: "remoteCluster",
ResourceRowStatus: resourceRowStatusUpdated,
ExpectedCluster: "remoteCluster",
ExpectedResourceRowStatus: resourceRowStatusUpdated,
},
}
for name, c := range cases {
mr := v1beta1.ManagedResource{
ClusterObjectReference: common.ClusterObjectReference{
Cluster: c.Cluster,
},
}
rr := buildResourceRow(mr, c.ResourceRowStatus)
r.Equal(c.ExpectedCluster, rr.mr.Cluster, name)
r.Equal(c.ExpectedResourceRowStatus, rr.status, name)
}
}

View File

@@ -86,9 +86,9 @@
limitBytes: *null | int
}
outputs?: {
logs: string
err?: string
info: {
logs?: string
err?: string
info?: {
fromDate: string
toDate: string
}

View File

@@ -35,7 +35,6 @@ import (
)
const (
errAuthenticateProvider = "failed to authenticate Terraform cloud provider %s for %s"
errProviderExists = "terraform provider %s for %s already exists"
errDeleteProvider = "failed to delete Terraform Provider %s err: %w"
errCouldNotDeleteProvider = "the Terraform Provider %s could not be disabled because it was created by enabling a Terraform provider or was manually created"
@@ -54,7 +53,7 @@ func CreateApplication(ctx context.Context, k8sClient client.Client, name, compo
if strings.HasPrefix(componentType, types.TerraformComponentPrefix) {
existed, err := IsTerraformProviderExisted(ctx, k8sClient, name)
if err != nil {
return errors.Wrapf(err, errAuthenticateProvider, name, componentType)
return errors.Wrapf(err, errCheckProviderExistence, name)
}
if existed {
return fmt.Errorf(errProviderExists, name, componentType)

View File

@@ -140,20 +140,16 @@ func (h *Helper) UpgradeChart(ch *chart.Chart, releaseName, namespace string, va
r.Info.Status == release.StatusPendingRollback {
return nil, fmt.Errorf("previous installation (e.g., using vela install or helm upgrade) is still in progress. Please try again in %d minutes", timeoutInMinutes)
}
}
// merge un-existing values into the values as user-input, because the helm chart upgrade didn't handle the new default values in the chart.
// the new default values <= the old custom values <= the new custom values
if config.ReuseValues {
// sort will sort the release by revision from old to new
relutil.SortByRevision(releases)
rel := releases[len(releases)-1]
// merge new chart values into old values, the values of old chart has the high priority
mergedWithNewValues := chartutil.CoalesceTables(rel.Chart.Values, ch.Values)
// merge the chart with the released chart config but follow the old config
mergeWithConfigs := chartutil.CoalesceTables(rel.Config, mergedWithNewValues)
// merge new values as the user input, follow the new user input for --set
values = chartutil.CoalesceTables(values, mergeWithConfigs)
values = chartutil.CoalesceTables(values, rel.Config)
}
// overwrite existing installation
@@ -161,7 +157,8 @@ func (h *Helper) UpgradeChart(ch *chart.Chart, releaseName, namespace string, va
install.Namespace = namespace
install.Wait = config.Wait
install.Timeout = time.Duration(timeoutInMinutes) * time.Minute
install.ReuseValues = config.ReuseValues
// use the new default value set.
install.ReuseValues = false
newRelease, err = install.Run(releaseName, ch, values)
}
// check if install/upgrade worked

View File

@@ -49,7 +49,10 @@ var _ = Describe("Test helm helper", func() {
helper := NewHelper()
chart, err := helper.LoadCharts("./testdata/autoscalertrait-0.1.0.tgz", nil)
Expect(err).Should(BeNil())
release, err := helper.UpgradeChart(chart, "autoscalertrait", "default", nil, UpgradeChartOptions{
release, err := helper.UpgradeChart(chart, "autoscalertrait", "default", map[string]interface{}{
"replicaCount": 2,
"image.tag": "0.1.0",
}, UpgradeChartOptions{
Config: cfg,
Detail: false,
Logging: util.IOStreams{Out: os.Stdout, ErrOut: os.Stderr},
@@ -58,6 +61,42 @@ var _ = Describe("Test helm helper", func() {
crds := GetCRDFromChart(release.Chart)
Expect(cmp.Diff(len(crds), 1)).Should(BeEmpty())
Expect(err).Should(BeNil())
deployments := GetDeploymentsFromManifest(release.Manifest)
Expect(cmp.Diff(len(deployments), 1)).Should(BeEmpty())
Expect(cmp.Diff(*deployments[0].Spec.Replicas, int32(2))).Should(BeEmpty())
containers := deployments[0].Spec.Template.Spec.Containers
Expect(cmp.Diff(len(containers), 1)).Should(BeEmpty())
// add new default value
Expect(cmp.Diff(containers[0].Image, "ghcr.io/oam-dev/catalog/autoscalertrait:0.1.0")).Should(BeEmpty())
chartNew, err := helper.LoadCharts("./testdata/autoscalertrait-0.2.0.tgz", nil)
Expect(err).Should(BeNil())
// the new custom values should override the last release custom values
releaseNew, err := helper.UpgradeChart(chartNew, "autoscalertrait", "default", map[string]interface{}{
"image.tag": "0.2.0",
}, UpgradeChartOptions{
Config: cfg,
Detail: false,
ReuseValues: true,
Logging: util.IOStreams{Out: os.Stdout, ErrOut: os.Stderr},
Wait: false,
})
Expect(err).Should(BeNil())
deployments = GetDeploymentsFromManifest(releaseNew.Manifest)
Expect(cmp.Diff(len(deployments), 1)).Should(BeEmpty())
// keep the custom values
Expect(cmp.Diff(*deployments[0].Spec.Replicas, int32(2))).Should(BeEmpty())
containers = deployments[0].Spec.Template.Spec.Containers
Expect(cmp.Diff(len(containers), 1)).Should(BeEmpty())
// change the default value
Expect(cmp.Diff(containers[0].Image, "ghcr.io/oam-dev/catalog/autoscalertrait:0.2.0")).Should(BeEmpty())
// add new default value
Expect(cmp.Diff(len(containers[0].Env), 1)).Should(BeEmpty())
Expect(cmp.Diff(containers[0].Env[0].Name, "env1")).Should(BeEmpty())
})
It("Test UninstallRelease", func() {

Binary file not shown.

Binary file not shown.

View File

@@ -1,89 +0,0 @@
/*
Copyright 2022 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package image
import (
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
// Meta is the struct for image metadata
type Meta struct {
Registry string
Repository string
Name string
Tag string
}
// DockerHubImageTagResponse is the struct for docker hub image tag response
type DockerHubImageTagResponse struct {
Count int `json:"count"`
Results []Result
}
// Result is the struct for docker hub image tag result
type Result struct {
Name string `json:"name"`
}
// IsExisted checks whether a public or private image exists
func IsExisted(username, password, image string) (bool, error) {
ref, err := name.ParseReference(image)
if err != nil {
return false, err
}
var img v1.Image
if username != "" || password != "" {
basic := &authn.Basic{
Username: username,
Password: password,
}
option := remote.WithAuth(basic)
img, err = remote.Image(ref, option)
if err != nil {
return false, err
}
} else {
img, err = remote.Image(ref)
if err != nil {
return false, err
}
}
_, err = img.Digest()
if err != nil {
return false, err
}
_, err = img.Manifest()
if err != nil {
return false, err
}
return true, nil
}
// RegistryMeta is the struct for registry metadata
type RegistryMeta struct {
Username string `json:"username"`
Password string `json:"password"`
}

View File

@@ -1,135 +0,0 @@
/*
Copyright 2022 The KubeVela Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package image
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestExisted(t *testing.T) {
type args struct {
username string
password string
image string
}
type want struct {
existed bool
errMsg string
}
testcases := map[string]struct {
args args
want want
}{
"empty image name": {
args: args{
image: "",
},
want: want{
existed: false,
errMsg: "could not parse referenc",
},
},
"just image name": {
args: args{
image: "nginx",
},
want: want{
existed: true,
},
},
" image name with registry": {
args: args{
image: "docker.io/nginx",
},
want: want{
existed: true,
},
},
"image name with tag": {
args: args{
image: "nginx:latest",
},
want: want{
existed: true,
},
},
"image name with repository": {
args: args{
image: "library/nginx:latest",
},
want: want{
existed: true,
},
},
"image name with registry and repository": {
args: args{
image: "docker.io/library/nginx",
},
want: want{
existed: true,
},
},
"invalid image name": {
args: args{
image: "jfsdfjwfjwf:fwefsfsjflwejfwjfoewfsffsfw",
},
want: want{
existed: false,
errMsg: "UNAUTHORIZED",
},
},
"invalid image registry": {
args: args{
image: "nginx1sf/jfsdfjwfjwf:fwefsfsjflwejfwjfoewfsffsfw",
},
want: want{
existed: false,
errMsg: "UNAUTHORIZED",
},
},
"not docker hub registry": {
args: args{
image: "alibabacloud.com/d/e:v0.1",
},
want: want{
existed: false,
errMsg: "invalid character '<' looking for beginning of value",
},
},
"private registry, invalid authentication": {
args: args{
image: "abcfefsfjflsfjweffwe73rr.com/d/e:v0.1",
username: "admin",
},
want: want{
existed: false,
errMsg: "Get",
},
},
}
for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
got, err := IsExisted(tc.args.username, tc.args.password, tc.args.image)
if err != nil || tc.want.errMsg != "" {
assert.Contains(t, err.Error(), tc.want.errMsg)
}
assert.Equal(t, got, tc.want.existed)
})
}
}

View File

@@ -22,6 +22,9 @@ import (
"strings"
"github.com/pkg/errors"
"github.com/oam-dev/kubevela/pkg/cue/model/value"
"github.com/oam-dev/kubevela/references/common"
)
// QueryView contains query data
@@ -40,6 +43,8 @@ const (
KeyWordView = "view"
// KeyWordParameter represent parameter keyword
KeyWordParameter = "parameter"
// KeyWordTemplate represents template keyword
KeyWordTemplate = "template"
// KeyWordExport represent export keyword
KeyWordExport = "export"
// DefaultExportValue is the default Export value
@@ -91,6 +96,36 @@ func ParseVelaQL(ql string) (QueryView, error) {
return qv, nil
}
// ParseVelaQLFromPath will parse a velaQL file path to QueryView
func ParseVelaQLFromPath(velaQLViewPath string) (*QueryView, error) {
body, err := common.ReadRemoteOrLocalPath(velaQLViewPath)
if err != nil {
return nil, errors.Errorf("read view file from %s: %v", velaQLViewPath, err)
}
val, err := value.NewValue(string(body), nil, "")
if err != nil {
return nil, errors.Errorf("new value for view: %v", err)
}
var expStr string
exp, err := val.LookupValue(KeyWordExport)
if err == nil {
expStr, err = exp.String()
if err != nil {
expStr = DefaultExportValue
}
} else {
expStr = DefaultExportValue
}
return &QueryView{
View: string(body),
Parameter: nil,
Export: strings.Trim(strings.TrimSpace(expStr), `"`),
}, nil
}
// ParseParameter parse parameter to map[string]interface{}
func ParseParameter(parameter string) (map[string]interface{}, error) {
parameter = strings.TrimLeft(parameter, "{")

View File

@@ -18,12 +18,12 @@ package query
import (
"bufio"
"bytes"
"context"
"encoding/base64"
"fmt"
"io"
"regexp"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
@@ -397,14 +397,6 @@ func (h *provider) GeneratorServiceEndpoints(wfctx wfContext.Context, v *value.V
return fillQueryResult(v, serviceEndpoints, "list")
}
var (
terminatedContainerNotFoundRegex = regexp.MustCompile("previous terminated container .+ in pod .+ not found")
)
func isTerminatedContainerNotFound(err error) bool {
return err != nil && terminatedContainerNotFoundRegex.MatchString(err.Error())
}
func (h *provider) CollectLogsInPod(ctx wfContext.Context, v *value.Value, act types.Action) error {
cluster, err := v.GetString("cluster")
if err != nil {
@@ -429,27 +421,29 @@ func (h *provider) CollectLogsInPod(ctx wfContext.Context, v *value.Value, act t
cliCtx := multicluster.ContextWithClusterName(context.Background(), cluster)
clientSet, err := kubernetes.NewForConfig(h.cfg)
if err != nil {
return errors.Wrapf(err, "failed to create kubernetes clientset")
return errors.Wrapf(err, "failed to create kubernetes client")
}
var defaultOutputs = make(map[string]interface{})
var errMsg string
podInst, err := clientSet.CoreV1().Pods(namespace).Get(cliCtx, pod, v1.GetOptions{})
if err != nil {
return errors.Wrapf(err, "failed to get pod")
errMsg = fmt.Sprintf("failed to get pod:%s", err.Error())
}
req := clientSet.CoreV1().Pods(namespace).GetLogs(pod, opts)
readCloser, err := req.Stream(cliCtx)
if err != nil && !isTerminatedContainerNotFound(err) {
return errors.Wrapf(err, "failed to get stream logs")
if err != nil {
errMsg = fmt.Sprintf("failed to get stream logs %s", err.Error())
}
r := bufio.NewReader(readCloser)
var b strings.Builder
var readErr error
if err == nil {
if readCloser != nil && podInst != nil {
r := bufio.NewReader(readCloser)
buffer := bytes.NewBuffer(nil)
var readErr error
defer func() {
_ = readCloser.Close()
}()
for {
s, err := r.ReadString('\n')
b.WriteString(s)
buffer.WriteString(s)
if err != nil {
if !errors.Is(err, io.EOF) {
readErr = err
@@ -457,30 +451,34 @@ func (h *provider) CollectLogsInPod(ctx wfContext.Context, v *value.Value, act t
break
}
}
} else {
readErr = err
toDate := v1.Now()
var fromDate v1.Time
// nolint
if opts.SinceTime != nil {
fromDate = *opts.SinceTime
} else if opts.SinceSeconds != nil {
fromDate = v1.NewTime(toDate.Add(time.Duration(-(*opts.SinceSeconds) * int64(time.Second))))
} else {
fromDate = podInst.CreationTimestamp
}
// the cue string can not support the special characters
logs := base64.StdEncoding.EncodeToString(buffer.Bytes())
defaultOutputs = map[string]interface{}{
"logs": logs,
"info": map[string]interface{}{
"fromDate": fromDate,
"toDate": toDate,
},
}
if readErr != nil {
errMsg = readErr.Error()
}
}
toDate := v1.Now()
var fromDate v1.Time
// nolint
if opts.SinceTime != nil {
fromDate = *opts.SinceTime
} else if opts.SinceSeconds != nil {
fromDate = v1.NewTime(toDate.Add(time.Duration(-(*opts.SinceSeconds) * int64(time.Second))))
} else {
fromDate = podInst.CreationTimestamp
if errMsg != "" {
klog.Warningf(errMsg)
defaultOutputs["err"] = errMsg
}
o := map[string]interface{}{
"logs": b.String(),
"info": map[string]interface{}{
"fromDate": fromDate,
"toDate": toDate,
},
}
if readErr != nil {
o["err"] = readErr.Error()
}
return v.FillObject(o, "outputs")
return v.FillObject(defaultOutputs, "outputs")
}
// Install register handlers to provider discover.

View File

@@ -23,6 +23,7 @@ import (
appsv1 "k8s.io/api/apps/v1"
v12 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
@@ -677,6 +678,10 @@ func iteratorChildResources(ctx context.Context, cluster string, k8sClient clien
clusterCTX := multicluster.ContextWithClusterName(ctx, cluster)
items, err := listItemByRule(clusterCTX, k8sClient, resource, *parentObject, specifiedFunc, rules.DefaultGenListOptionFunc)
if err != nil {
if meta.IsNoMatchError(err) || runtime.IsNotRegisteredError(err) {
log.Logger.Errorf("error to list subresources: %s err: %v", resource.Kind, err)
continue
}
return nil, err
}
for _, item := range items {

View File

@@ -1380,6 +1380,54 @@ var _ = Describe("unit-test to e2e test", func() {
Expect(err).Should(BeNil())
Expect(len(res.List)).Should(Equal(2))
})
It("Test not exist api don't break whole process", func() {
notExistRuleStr := `
- parentResourceType:
group: apps
kind: Deployment
childrenResourceType:
- apiVersion: v2
kind: Pod
`
notExistParentResourceStr := `
- parentResourceType:
group: badgroup
kind: Deployment
childrenResourceType:
- apiVersion: v2
kind: Pod
`
Expect(k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
badRuleConfigMap := v1.ConfigMap{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "ConfigMap"},
ObjectMeta: metav1.ObjectMeta{Namespace: types3.DefaultKubeVelaNS, Name: "bad-rule", Labels: map[string]string{oam.LabelResourceRules: "true"}},
Data: map[string]string{relationshipKey: notExistRuleStr},
}
Expect(k8sClient.Create(ctx, &badRuleConfigMap)).Should(BeNil())
notExistParentConfigMap := v1.ConfigMap{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "ConfigMap"},
ObjectMeta: metav1.ObjectMeta{Namespace: types3.DefaultKubeVelaNS, Name: "not-exist-parent", Labels: map[string]string{oam.LabelResourceRules: "true"}},
Data: map[string]string{relationshipKey: notExistParentResourceStr},
}
Expect(k8sClient.Create(ctx, &notExistParentConfigMap)).Should(BeNil())
prd := provider{cli: k8sClient}
opt := `app: {
name: "app"
namespace: "test-namespace"
}`
v, err := value.NewValue(opt, nil, "")
Expect(err).Should(BeNil())
Expect(prd.GetApplicationResourceTree(nil, v, nil)).Should(BeNil())
type Res struct {
List []types.AppliedResource `json:"list"`
}
var res Res
err = v.UnmarshalTo(&res)
Expect(err).Should(BeNil())
Expect(len(res.List)).Should(Equal(2))
})
})
var _ = Describe("test merge globalRules", func() {

View File

@@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -37,6 +38,7 @@ import (
"github.com/oam-dev/kubevela/pkg/utils"
"github.com/oam-dev/kubevela/pkg/utils/apply"
"github.com/oam-dev/kubevela/pkg/workflow/tasks"
"github.com/oam-dev/kubevela/pkg/workflow/tasks/template"
wfTypes "github.com/oam-dev/kubevela/pkg/workflow/types"
)
@@ -73,7 +75,7 @@ func (handler *ViewHandler) QueryView(ctx context.Context, qv QueryView) (*value
outputsTemplate := fmt.Sprintf(OutputsTemplate, qv.Export, qv.Export)
queryKey := QueryParameterKey{}
if err := json.Unmarshal([]byte(outputsTemplate), &queryKey); err != nil {
return nil, err
return nil, errors.Errorf("unmarhsal query template: %v", err)
}
handler.viewTask = v1beta1.WorkflowStep{
@@ -84,7 +86,12 @@ func (handler *ViewHandler) QueryView(ctx context.Context, qv QueryView) (*value
}
pCtx := process.NewContext(process.ContextData{})
taskDiscover := tasks.NewViewTaskDiscover(handler.pd, handler.cli, handler.cfg, handler.dispatch, handler.delete, handler.namespace, 3, pCtx)
loader := template.NewViewTemplateLoader(handler.cli, handler.namespace)
if len(strings.Split(qv.View, "\n")) > 2 {
loader = &template.EchoLoader{}
}
taskDiscover := tasks.NewViewTaskDiscover(handler.pd, handler.cli, handler.cfg, handler.dispatch, handler.delete, handler.namespace, 3, pCtx, loader)
genTask, err := taskDiscover.GetTaskGenerator(ctx, handler.viewTask.Type)
if err != nil {
return nil, err
@@ -97,14 +104,14 @@ func (handler *ViewHandler) QueryView(ctx context.Context, qv QueryView) (*value
viewCtx, err := NewViewContext()
if err != nil {
return nil, err
return nil, errors.Errorf("new view context: %v", err)
}
status, _, err := runner.Run(viewCtx, &wfTypes.TaskRunOptions{})
if err != nil {
return nil, err
return nil, errors.Errorf("run query view: %v", err)
}
if string(status.Phase) != ViewTaskPhaseSucceeded {
return nil, errors.Errorf("failed to query the view %s", status.Message)
return nil, errors.Errorf("failed to query the view: %s", status.Message)
}
return viewCtx.GetVar(qv.Export)
}

View File

@@ -272,7 +272,7 @@ func (tr *stepGroupTaskRunner) Run(ctx wfContext.Context, options *types.TaskRun
}
// NewViewTaskDiscover will create a client for load task generator.
func NewViewTaskDiscover(pd *packages.PackageDiscover, cli client.Client, cfg *rest.Config, apply kube.Dispatcher, delete kube.Deleter, viewNs string, logLevel int, pCtx process.Context) types.TaskDiscover {
func NewViewTaskDiscover(pd *packages.PackageDiscover, cli client.Client, cfg *rest.Config, apply kube.Dispatcher, delete kube.Deleter, viewNs string, logLevel int, pCtx process.Context, loader template.Loader) types.TaskDiscover {
handlerProviders := providers.NewProviders()
// install builtin provider
@@ -282,10 +282,9 @@ func NewViewTaskDiscover(pd *packages.PackageDiscover, cli client.Client, cfg *r
http.Install(handlerProviders, cli, viewNs)
email.Install(handlerProviders)
templateLoader := template.NewViewTemplateLoader(cli, viewNs)
return &taskDiscover{
remoteTaskDiscover: custom.NewTaskLoader(templateLoader.LoadTaskTemplate, pd, handlerProviders, logLevel, pCtx),
templateLoader: templateLoader,
remoteTaskDiscover: custom.NewTaskLoader(loader.LoadTaskTemplate, pd, handlerProviders, logLevel, pCtx),
templateLoader: loader,
}
}

View File

@@ -119,3 +119,12 @@ func NewViewTemplateLoader(client client.Client, namespace string) Loader {
namespace: namespace,
}
}
// EchoLoader will load data from input as it is.
type EchoLoader struct {
}
// LoadTaskTemplate gets the echo content exactly what it is .
func (ll *EchoLoader) LoadTaskTemplate(_ context.Context, content string) (string, error) {
return content, nil
}

View File

@@ -38,9 +38,12 @@ const (
addonGitToken = "gitToken"
addonOssType = "OSS"
addonGitType = "git"
addonGiteeType = "gitee"
addonGitlabType = "gitlab"
addonHelmType = "helm"
addonUsername = "username"
addonPassword = "password"
addonRepoName = "repoName"
)
// NewAddonRegistryCommand return an addon registry command
@@ -223,12 +226,25 @@ func getAddonRegistry(ctx context.Context, c common.Args, name string) error {
return err
}
table := uitable.New()
if registry.OSS != nil {
switch {
case registry.OSS != nil:
table.AddRow("NAME", "Type", "ENDPOINT", "BUCKET", "PATH")
table.AddRow(registry.Name, "OSS", registry.OSS.Endpoint, registry.OSS.Bucket, registry.OSS.Path)
} else {
case registry.Helm != nil:
table.AddRow("NAME", "Type", "ENDPOINT")
table.AddRow(registry.Name, "Helm", registry.Helm.URL)
case registry.Gitee != nil:
table.AddRow("NAME", "Type", "ENDPOINT", "PATH")
table.AddRow(registry.Name, "git", registry.Git.URL, registry.Git.Path)
table.AddRow(registry.Name, "Gitee", registry.Gitee.URL, registry.Gitee.Path)
case registry.Gitlab != nil:
table.AddRow("NAME", "Type", "ENDPOINT", "REPOSITORY", "PATH")
table.AddRow(registry.Name, "Gitlab", registry.Gitlab.URL, registry.Gitlab.Repo, registry.Gitlab.Path)
case registry.Git != nil:
table.AddRow("NAME", "Type", "ENDPOINT", "PATH")
table.AddRow(registry.Name, "Git", registry.Git.URL, registry.Git.Path)
default:
table.AddRow("Name")
table.AddRow(registry.Name)
}
fmt.Println(table.String())
return nil
@@ -331,6 +347,37 @@ func getRegistryFromArgs(cmd *cobra.Command, args []string) (*pkgaddon.Registry,
return nil, err
}
r.Git.Token = token
case addonGiteeType:
r.Gitee = &pkgaddon.GiteeAddonSource{}
r.Gitee.URL = endpoint
path, err := cmd.Flags().GetString(addonPath)
if err != nil {
return nil, err
}
r.Gitee.Path = path
token, err := cmd.Flags().GetString(addonGitToken)
if err != nil {
return nil, err
}
r.Gitee.Token = token
case addonGitlabType:
r.Gitlab = &pkgaddon.GitlabAddonSource{}
r.Gitlab.URL = endpoint
path, err := cmd.Flags().GetString(addonPath)
if err != nil {
return nil, err
}
r.Gitlab.Path = path
token, err := cmd.Flags().GetString(addonGitToken)
if err != nil {
return nil, err
}
r.Gitlab.Token = token
gitLabRepoName, err := cmd.Flags().GetString(addonRepoName)
if err != nil {
return nil, err
}
r.Gitlab.Repo = gitLabRepoName
case addonHelmType:
r.Helm = &pkgaddon.HelmSource{}
r.Helm.URL = endpoint

View File

@@ -30,6 +30,7 @@ import (
"helm.sh/helm/v3/pkg/strvals"
"github.com/oam-dev/kubevela/pkg/apiserver/domain/service"
"github.com/oam-dev/kubevela/pkg/oam"
"k8s.io/client-go/rest"
@@ -75,6 +76,8 @@ var addonClusters string
var verboseSatatus bool
var skipValidate bool
// NewAddonCommand create `addon` command
func NewAddonCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
@@ -188,6 +191,7 @@ Enable addon for specific clusters, (local means control plane):
cmd.Flags().StringVarP(&addonVersion, "version", "v", "", "specify the addon version to enable")
cmd.Flags().StringVarP(&addonClusters, types.ClustersArg, "c", "", "specify the runtime-clusters to enable")
cmd.Flags().BoolVarP(&skipValidate, "skip-version-validating", "s", false, "skip validating system version requirement")
return cmd
}
@@ -201,8 +205,7 @@ func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient cli
}
if name == "velaux" {
if !isUpgrade {
fmt.Println(`To check the initialized admin user name and password by:`)
fmt.Println(` vela logs -n vela-system --name apiserver addon-velaux | grep "initialized admin username"`)
fmt.Printf("Initialized admin username and password: admin / %s \n", service.InitAdminPassword)
}
fmt.Println(`To open the dashboard directly by port-forward:`)
fmt.Println(` vela port-forward -n vela-system addon-velaux 9082:80`)
@@ -285,6 +288,7 @@ Upgrade addon for specific clusters, (local means control plane):
},
}
cmd.Flags().StringVarP(&addonVersion, "version", "v", "", "specify the addon version to upgrade")
cmd.Flags().BoolVarP(&skipValidate, "skip-version-validating", "s", false, "skip validating system version requirement")
return cmd
}
@@ -362,7 +366,11 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
}
for _, registry := range registries {
err = pkgaddon.EnableAddon(ctx, name, version, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil)
var opts []pkgaddon.InstallOption
if skipValidate {
opts = append(opts, pkgaddon.SkipValidateVersion)
}
err = pkgaddon.EnableAddon(ctx, name, version, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, opts...)
if errors.Is(err, pkgaddon.ErrNotExist) {
continue
}
@@ -382,7 +390,11 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
// enableAddonByLocal enable addon in local dir and return the addon name
func enableAddonByLocal(ctx context.Context, name string, dir string, k8sClient client.Client, dc *discovery.DiscoveryClient, config *rest.Config, args map[string]interface{}) error {
if err := pkgaddon.EnableAddonByLocalDir(ctx, name, dir, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, args); err != nil {
var opts []pkgaddon.InstallOption
if skipValidate {
opts = append(opts, pkgaddon.SkipValidateVersion)
}
if err := pkgaddon.EnableAddonByLocalDir(ctx, name, dir, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, args, opts...); err != nil {
return err
}
if err := waitApplicationRunning(k8sClient, name); err != nil {

View File

@@ -71,7 +71,7 @@ func NewCommandWithIOStreams(ioStream util.IOStreams) *cobra.Command {
commandArgs := common.Args{
Schema: common.Scheme,
}
f := velacmd.NewDefaultFactory(config.GetConfigOrDie())
f := velacmd.NewDeferredFactory(config.GetConfig)
if err := system.InitDirs(); err != nil {
fmt.Println("InitDir err", err)

View File

@@ -24,6 +24,7 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/meta"
"github.com/fatih/color"
prismclusterv1alpha1 "github.com/kubevela/prism/pkg/apis/cluster/v1alpha1"
"github.com/oam-dev/cluster-gateway/pkg/config"
"github.com/oam-dev/cluster-gateway/pkg/generated/clientset/versioned"
"github.com/pkg/errors"
@@ -107,11 +108,11 @@ func NewClusterListCommand(c *common.Args) *cobra.Command {
if err != nil {
return err
}
clusters, err := multicluster.ListVirtualClusters(context.Background(), client)
clusters, err := prismclusterv1alpha1.NewClusterClient(client).List(context.Background())
if err != nil {
return errors.Wrap(err, "fail to get registered cluster")
}
for _, cluster := range clusters {
for _, cluster := range clusters.Items {
var labels []string
for k, v := range cluster.Labels {
if !strings.HasPrefix(k, config.MetaApiGroupName) {
@@ -124,7 +125,7 @@ func NewClusterListCommand(c *common.Args) *cobra.Command {
}
for i, l := range labels {
if i == 0 {
table.AddRow(cluster.Name, cluster.Alias, cluster.Type, cluster.EndPoint, fmt.Sprintf("%v", cluster.Accepted), l)
table.AddRow(cluster.Name, cluster.Spec.Alias, cluster.Spec.CredentialType, cluster.Spec.Endpoint, fmt.Sprintf("%v", cluster.Spec.Accepted), l)
} else {
table.AddRow("", "", "", "", "", l)
}

View File

@@ -226,7 +226,7 @@ func PrintInstalledCompDef(c common2.Args, io cmdutil.IOStreams, filter filterFu
}
table := newUITable()
table.AddRow("NAME", "DEFINITION")
table.AddRow("NAME", "DEFINITION", "DESCRIPTION")
for _, cd := range list.Items {
data, err := json.Marshal(cd)
@@ -242,7 +242,7 @@ func PrintInstalledCompDef(c common2.Args, io cmdutil.IOStreams, filter filterFu
if filter != nil && !filter(capa) {
continue
}
table.AddRow(capa.Name, capa.CrdName)
table.AddRow(capa.Name, capa.CrdName, capa.Description)
}
io.Info(table.String())
return nil

View File

@@ -20,7 +20,9 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/gosuri/uitable"
tcv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
@@ -56,19 +58,11 @@ func NewProviderCommand(c common.Args, order string, ioStreams cmdutil.IOStreams
types.TagCommandType: types.TypeExtension,
},
}
add, err := prepareProviderAddCommand(c, ioStreams)
if err == nil {
cmd.AddCommand(add)
}
delete, err := prepareProviderDeleteCommand(c, ioStreams)
if err == nil {
cmd.AddCommand(delete)
}
cmd.AddCommand(
NewProviderListCommand(c, ioStreams),
)
cmd.AddCommand(prepareProviderAddCommand(c, ioStreams))
cmd.AddCommand(prepareProviderDeleteCommand(c, ioStreams))
return cmd
}
@@ -93,13 +87,7 @@ func NewProviderListCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.C
}
}
func prepareProviderAddCommand(c common.Args, ioStreams cmdutil.IOStreams) (*cobra.Command, error) {
ctx := context.Background()
k8sClient, err := c.GetClient()
if err != nil {
return nil, err
}
func prepareProviderAddCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "add",
Short: "Authenticate Terraform Cloud Provider",
@@ -107,13 +95,13 @@ func prepareProviderAddCommand(c common.Args, ioStreams cmdutil.IOStreams) (*cob
Example: "vela provider add <provider-type>",
}
addSubCommands, err := prepareProviderAddSubCommand(c, ioStreams)
if err != nil {
return nil, err
}
cmd.AddCommand(addSubCommands...)
cmd.RunE = func(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
defer cancel()
k8sClient, err := c.GetClient()
if err != nil {
return err
}
defs, err := getTerraformProviderTypes(ctx, k8sClient)
if len(args) < 1 {
errMsg := "must specify a Terraform Cloud Provider type"
@@ -143,11 +131,21 @@ func prepareProviderAddCommand(c common.Args, ioStreams cmdutil.IOStreams) (*cob
}
return nil
}
return cmd, nil
addSubCommands, err := prepareProviderAddSubCommand(c, ioStreams)
if err != nil {
ioStreams.Errorf("Fail to prepare the sub commands for the add command:%s \n", err.Error())
}
cmd.AddCommand(addSubCommands...)
return cmd
}
func prepareProviderAddSubCommand(c common.Args, ioStreams cmdutil.IOStreams) ([]*cobra.Command, error) {
ctx := context.Background()
if len(os.Args) < 2 || os.Args[1] != "provider" {
return nil, nil
}
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
defer cancel()
k8sClient, err := c.GetClient()
if err != nil {
return nil, err
@@ -168,9 +166,12 @@ func prepareProviderAddSubCommand(c common.Args, ioStreams cmdutil.IOStreams) ([
return nil, err
}
for _, p := range parameters {
// TODO(wonderflow): make the provider default name to be unique but keep the compatiblility as some Application didn't specify the name,
// now it's “default” for every one, the name will conflict if we have more than one cloud provider.
cmd.Flags().String(p.Name, fmt.Sprint(p.Default), p.Usage)
}
cmd.RunE = func(cmd *cobra.Command, args []string) error {
newContext := context.Background()
name, err := cmd.Flags().GetString(providerNameParam)
if err != nil || name == "" {
return fmt.Errorf("must specify a name for the Terraform Cloud Provider %s", providerType)
@@ -190,8 +191,7 @@ func prepareProviderAddSubCommand(c common.Args, ioStreams cmdutil.IOStreams) ([
if err != nil {
return fmt.Errorf(errAuthenticateProvider, providerType, err)
}
if err := config.CreateApplication(ctx, k8sClient, name, providerType, string(data), config.UIParam{}); err != nil {
if err := config.CreateApplication(newContext, k8sClient, name, providerType, string(data), config.UIParam{}); err != nil {
return fmt.Errorf(errAuthenticateProvider, providerType, err)
}
ioStreams.Infof("Successfully authenticate provider %s for %s\n", name, providerType)
@@ -315,28 +315,28 @@ func getTerraformProviderType(ctx context.Context, k8sClient client.Client, name
return def, nil
}
func prepareProviderDeleteCommand(c common.Args, ioStreams cmdutil.IOStreams) (*cobra.Command, error) {
ctx := context.Background()
k8sClient, err := c.GetClient()
if err != nil {
return nil, err
}
func prepareProviderDeleteCommand(c common.Args, ioStreams cmdutil.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
Aliases: []string{"rm", "del"},
Short: "Delete Terraform Cloud Provider",
Long: "Delete Terraform Cloud Provider",
Example: "vela provider delete <provider-type> -name <provider-name>",
Example: "vela provider delete <provider-type> --name <provider-name>",
}
deleteSubCommands, err := prepareProviderDeleteSubCommand(c, ioStreams)
if err != nil {
return nil, err
ioStreams.Errorf("Fail to prepare the sub commands for the delete command:%s \n", err.Error())
}
cmd.AddCommand(deleteSubCommands...)
cmd.RunE = func(cmd *cobra.Command, args []string) error {
k8sClient, err := c.GetClient()
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
defer cancel()
defs, err := getTerraformProviderTypes(ctx, k8sClient)
if len(args) < 1 {
errMsg := "must specify a Terraform Cloud Provider type"
@@ -366,11 +366,15 @@ func prepareProviderDeleteCommand(c common.Args, ioStreams cmdutil.IOStreams) (*
}
return nil
}
return cmd, nil
return cmd
}
func prepareProviderDeleteSubCommand(c common.Args, ioStreams cmdutil.IOStreams) ([]*cobra.Command, error) {
ctx := context.Background()
if len(os.Args) < 2 || os.Args[1] != "provider" {
return nil, nil
}
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
defer cancel()
k8sClient, err := c.GetClient()
if err != nil {
return nil, err

View File

@@ -100,12 +100,6 @@ func startReferenceDocsSite(ctx context.Context, ns string, c common.Args, ioStr
}
referenceHome := filepath.Join(home, "reference")
definitionPath := filepath.Join(referenceHome, "capabilities")
if _, err := os.Stat(definitionPath); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(definitionPath, 0750); err != nil {
return err
}
}
docsPath := filepath.Join(referenceHome, "docs")
if _, err := os.Stat(docsPath); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(docsPath, 0750); err != nil {
@@ -171,22 +165,12 @@ func startReferenceDocsSite(ctx context.Context, ns string, c common.Args, ioStr
return err
}
var capabilityPath string
switch capabilityType {
case types.TypeWorkload:
capabilityPath = plugins.WorkloadTypePath
case types.TypeTrait:
capabilityPath = plugins.TraitPath
case types.TypeScope:
case types.TypeComponentDefinition:
capabilityPath = plugins.ComponentDefinitionTypePath
case types.TypeWorkflowStep:
capabilityPath = plugins.WorkflowStepPath
default:
if capabilityType != types.TypeWorkload && capabilityType != types.TypeTrait && capabilityType != types.TypeScope &&
capabilityType != types.TypeComponentDefinition && capabilityType != types.TypeWorkflowStep {
return fmt.Errorf("unsupported type: %v", capabilityType)
}
url := fmt.Sprintf("http://127.0.0.1%s/#/%s/%s", Port, capabilityPath, capabilityName)
url := fmt.Sprintf("http://127.0.0.1%s/#/%s/%s", Port, capabilityType, capabilityName)
server := &http.Server{
Addr: Port,
Handler: http.FileServer(http.Dir(docsPath)),
@@ -227,7 +211,7 @@ func launch(server *http.Server, errChan chan<- error) {
func generateSideBar(capabilities []types.Capability, docsPath string) error {
sideBar := filepath.Join(docsPath, SideBar)
components, traits, workflowsteps := getDefinitions(capabilities)
components, traits, workflowSteps, policies := getDefinitions(capabilities)
f, err := os.Create(sideBar)
if err != nil {
return err
@@ -235,8 +219,9 @@ func generateSideBar(capabilities []types.Capability, docsPath string) error {
if _, err := f.WriteString("- Components Types\n"); err != nil {
return err
}
for _, c := range components {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", c, plugins.ComponentDefinitionTypePath, c)); err != nil {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", c, types.TypeComponentDefinition, c)); err != nil {
return err
}
}
@@ -244,15 +229,24 @@ func generateSideBar(capabilities []types.Capability, docsPath string) error {
return err
}
for _, t := range traits {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, plugins.TraitPath, t)); err != nil {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, types.TypeTrait, t)); err != nil {
return err
}
}
if _, err := f.WriteString("- Workflow Steps\n"); err != nil {
return err
}
for _, t := range workflowsteps {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, plugins.WorkflowStepPath, t)); err != nil {
for _, t := range workflowSteps {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, types.TypeWorkflowStep, t)); err != nil {
return err
}
}
if _, err := f.WriteString("- Policies\n"); err != nil {
return err
}
for _, t := range policies {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, types.TypePolicy, t)); err != nil {
return err
}
}
@@ -327,14 +321,14 @@ func generateREADME(capabilities []types.Capability, docsPath string) error {
return err
}
workloads, traits, workflowsteps := getDefinitions(capabilities)
workloads, traits, workflowSteps, policies := getDefinitions(capabilities)
if _, err := f.WriteString("## Component Types\n"); err != nil {
return err
}
for _, w := range workloads {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", w, plugins.ComponentDefinitionTypePath, w)); err != nil {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", w, types.TypeComponentDefinition, w)); err != nil {
return err
}
}
@@ -343,7 +337,7 @@ func generateREADME(capabilities []types.Capability, docsPath string) error {
}
for _, t := range traits {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, plugins.TraitPath, t)); err != nil {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, types.TypeTrait, t)); err != nil {
return err
}
}
@@ -351,16 +345,26 @@ func generateREADME(capabilities []types.Capability, docsPath string) error {
if _, err := f.WriteString("## Workflow Steps\n"); err != nil {
return err
}
for _, t := range workflowsteps {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, plugins.WorkflowStepPath, t)); err != nil {
for _, t := range workflowSteps {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, types.TypeWorkflowStep, t)); err != nil {
return err
}
}
if _, err := f.WriteString("## Policies\n"); err != nil {
return err
}
for _, t := range policies {
if _, err := f.WriteString(fmt.Sprintf(" - [%s](%s/%s.md)\n", t, types.TypePolicy, t)); err != nil {
return err
}
}
return nil
}
func getDefinitions(capabilities []types.Capability) ([]string, []string, []string) {
var components, traits, workflowSteps []string
func getDefinitions(capabilities []types.Capability) ([]string, []string, []string, []string) {
var components, traits, workflowSteps, policies []string
for _, c := range capabilities {
switch c.Type {
case types.TypeComponentDefinition:
@@ -369,12 +373,14 @@ func getDefinitions(capabilities []types.Capability) ([]string, []string, []stri
traits = append(traits, c.Name)
case types.TypeWorkflowStep:
workflowSteps = append(workflowSteps, c.Name)
case types.TypePolicy:
policies = append(policies, c.Name)
case types.TypeScope:
case types.TypeWorkload:
default:
}
}
return components, traits, workflowSteps
return components, traits, workflowSteps, policies
}
// ShowReferenceConsole will show capability reference in console

View File

@@ -27,7 +27,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/references/plugins"
)
const BaseDir = "testdata"
@@ -134,9 +133,9 @@ func TestGenerateREADME(t *testing.T) {
for _, c := range tc.capabilities {
switch c.Type {
case types.TypeComponentDefinition:
assert.Contains(t, string(data), fmt.Sprintf(" - [%s](%s/%s.md)\n", c.Name, plugins.ComponentDefinitionTypePath, c.Name))
assert.Contains(t, string(data), fmt.Sprintf(" - [%s](%s/%s.md)\n", c.Name, types.TypeComponentDefinition, c.Name))
case types.TypeTrait:
assert.Contains(t, string(data), fmt.Sprintf(" - [%s](%s/%s.md)\n", c.Name, plugins.TraitPath, c.Name))
assert.Contains(t, string(data), fmt.Sprintf(" - [%s](%s/%s.md)\n", c.Name, types.TypeTrait, c.Name))
}
}
})
@@ -147,10 +146,15 @@ func TestGetWorkloadAndTraits(t *testing.T) {
type want struct {
workloads []string
traits []string
policies []string
}
workloadName := "component1"
traitName := "trait1"
scopeName := "scope1"
var (
workloadName = "component1"
traitName = "trait1"
scopeName = "scope1"
policyName = "policy1"
)
cases := map[string]struct {
reason string
@@ -187,11 +191,22 @@ func TestGetWorkloadAndTraits(t *testing.T) {
traits: nil,
},
},
"PolicyTypeCapability": {
capabilities: []types.Capability{
{
Name: policyName,
Type: types.TypePolicy,
},
},
want: want{
policies: []string{policyName},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
gotWorkloads, gotTraits, _ := getDefinitions(tc.capabilities)
assert.Equal(t, tc.want, want{workloads: gotWorkloads, traits: gotTraits})
gotWorkloads, gotTraits, _, gotPolicies := getDefinitions(tc.capabilities)
assert.Equal(t, tc.want, want{workloads: gotWorkloads, traits: gotTraits, policies: gotPolicies})
})
}
}

View File

@@ -159,7 +159,6 @@ func printAppStatus(_ context.Context, c client.Client, ioStreams cmdutil.IOStre
if err := printWorkflowStatus(c, ioStreams, appName, namespace); err != nil {
return err
}
cmd.Printf("Services:\n\n")
return loopCheckStatus(c, ioStreams, appName, namespace)
}
@@ -237,6 +236,9 @@ func loopCheckStatus(c client.Client, ioStreams cmdutil.IOStreams, appName strin
if err != nil {
return err
}
if len(remoteApp.Status.Services) > 0 {
ioStreams.Infof("Services:\n\n")
}
for _, comp := range remoteApp.Status.Services {
compName := comp.Name
envStat := ""

View File

@@ -228,6 +228,7 @@ func PrintInstalledTraitDef(c common2.Args, io cmdutil.IOStreams, filter filterF
table := newUITable()
table.AddRow("NAME", "APPLIES-TO")
table.AddRow("NAME", "APPLIES-TO", "DESCRIPTION")
for _, td := range list.Items {
data, err := json.Marshal(td)
@@ -243,7 +244,7 @@ func PrintInstalledTraitDef(c common2.Args, io cmdutil.IOStreams, filter filterF
if filter != nil && !filter(capa) {
continue
}
table.AddRow(capa.Name, capa.AppliesTo)
table.AddRow(capa.Name, capa.AppliesTo, capa.Description)
}
io.Info(table.String())
return nil

View File

@@ -209,6 +209,21 @@ func forceDisableAddon(ctx context.Context, kubeClient client.Client, config *re
if err := pkgaddon.DisableAddon(ctx, kubeClient, "fluxcd", config, true); err != nil {
return err
}
timeConsumed = time.Now()
for {
if time.Now().After(timeConsumed.Add(5 * time.Minute)) {
return errors.New("timeout disable fluxcd addon, please disable the addon manually")
}
addons, err := checkInstallAddon(kubeClient)
if err != nil {
return err
}
if len(addons) == 0 {
break
}
fmt.Printf("Waiting delete the fluxcd addon, timeout left %s . \r\n", 5*time.Minute-time.Since(timeConsumed))
time.Sleep(2 * time.Second)
}
}
return nil
}

View File

@@ -21,6 +21,7 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/spf13/cobra"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -42,60 +43,116 @@ type Filter struct {
// NewQlCommand creates `ql` command for executing velaQL
func NewQlCommand(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command {
var cueFile, querySts string
ctx := context.Background()
cmd := &cobra.Command{
Use: "ql",
Short: "Show result of executing velaQL.",
Long: "Show result of executing velaQL.",
Example: `vela ql "view{parameter=value1,parameter=value2}"`,
Use: "ql",
Short: "Show result of executing velaQL.",
Long: `Show result of executing velaQL, use it like:
vela ql --query "<inner-view-name>{<param1>=<value1>,<param2>=<value2>}
vela ql --file ./ql.cue
`,
Example: `Users can query with a query statement:
vela ql --query "<inner-view-name>{<param1>=<value1>,<param2>=<value2>}"
They can also query by a ql file:
vela ql --file ./ql.cue
Example content of ql.cue:
---
import (
"vela/ql"
)
configmap: ql.#Read & {
value: {
kind: "ConfigMap"
apiVersion: "v1"
metadata: {
name: "mycm"
}
}
}
status: configmap.value.data.key
export: "status"
---
`,
RunE: func(cmd *cobra.Command, args []string) error {
argsLength := len(args)
if argsLength == 0 {
return fmt.Errorf("please specify an VelaQL statement")
if cueFile == "" && querySts == "" && len(args) == 0 {
return fmt.Errorf("please specify at least on VelaQL statement or velaql file path")
}
velaQL := args[0]
newClient, err := c.GetClient()
if err != nil {
return err
}
return printVelaQLResult(ctx, newClient, c, velaQL, cmd)
if cueFile != "" {
return queryFromView(ctx, newClient, c, cueFile, cmd)
}
if querySts == "" {
// for compatibility
querySts = args[0]
}
return queryFromStatement(ctx, newClient, c, querySts, cmd)
},
Annotations: map[string]string{
types.TagCommandOrder: order,
types.TagCommandType: types.TypeApp,
},
}
cmd.Flags().StringVarP(&cueFile, "file", "f", "", "The CUE file path for VelaQL, it could be a remote url.")
cmd.Flags().StringVarP(&querySts, "query", "q", "", "The query statement for VelaQL.")
cmd.SetOut(ioStreams.Out)
return cmd
}
// printVelaQLResult show velaQL result
func printVelaQLResult(ctx context.Context, client client.Client, velaC common.Args, velaQL string, cmd *cobra.Command) error {
queryValue, err := QueryValue(ctx, client, velaC, velaQL)
// queryFromStatement print velaQL result from query statement with inner query view
func queryFromStatement(ctx context.Context, client client.Client, velaC common.Args, velaQLStatement string, cmd *cobra.Command) error {
queryView, err := velaql.ParseVelaQL(velaQLStatement)
if err != nil {
return err
}
queryValue, err := QueryValue(ctx, client, velaC, &queryView)
if err != nil {
return err
}
return print(queryValue, cmd)
}
// queryFromView print velaQL result from query view
func queryFromView(ctx context.Context, client client.Client, velaC common.Args, velaQLViewPath string, cmd *cobra.Command) error {
queryView, err := velaql.ParseVelaQLFromPath(velaQLViewPath)
if err != nil {
return err
}
queryValue, err := QueryValue(ctx, client, velaC, queryView)
if err != nil {
return err
}
return print(queryValue, cmd)
}
func print(queryValue *value.Value, cmd *cobra.Command) error {
response, err := queryValue.CueValue().MarshalJSON()
if err != nil {
return err
}
var out bytes.Buffer
err = json.Indent(&out, response, "", " ")
err = json.Indent(&out, response, "", " ")
if err != nil {
return err
}
cmd.Printf("%s\n", out.String())
cmd.Println(strings.Trim(strings.TrimSpace(out.String()), "\""))
return nil
}
// MakeVelaQL build velaQL
func MakeVelaQL(view string, params map[string]string, action string) string {
var paramString string
for key, value := range params {
for k, v := range params {
if paramString != "" {
paramString = fmt.Sprintf("%s, %s=%s", paramString, key, value)
paramString = fmt.Sprintf("%s, %s=%s", paramString, k, v)
} else {
paramString = fmt.Sprintf("%s=%s", key, value)
paramString = fmt.Sprintf("%s=%s", k, v)
}
}
return fmt.Sprintf("%s{%s}.%s", view, paramString, action)
@@ -116,7 +173,11 @@ func GetServiceEndpoints(ctx context.Context, client client.Client, appName stri
}
velaQL := MakeVelaQL("service-endpoints-view", params, "status")
queryValue, err := QueryValue(ctx, client, velaC, velaQL)
queryView, err := velaql.ParseVelaQL(velaQL)
if err != nil {
return nil, err
}
queryValue, err := QueryValue(ctx, client, velaC, &queryView)
if err != nil {
return nil, err
}
@@ -134,7 +195,7 @@ func GetServiceEndpoints(ctx context.Context, client client.Client, appName stri
}
// QueryValue get queryValue from velaQL
func QueryValue(ctx context.Context, client client.Client, velaC common.Args, velaQL string) (*value.Value, error) {
func QueryValue(ctx context.Context, client client.Client, velaC common.Args, queryView *velaql.QueryView) (*value.Value, error) {
dm, err := velaC.GetDiscoveryMapper()
if err != nil {
return nil, err
@@ -143,15 +204,11 @@ func QueryValue(ctx context.Context, client client.Client, velaC common.Args, ve
if err != nil {
return nil, err
}
queryView, err := velaql.ParseVelaQL(velaQL)
if err != nil {
return nil, err
}
config, err := velaC.GetConfig()
if err != nil {
return nil, err
}
queryValue, err := velaql.NewViewHandler(client, config, dm, pd).QueryView(ctx, queryView)
queryValue, err := velaql.NewViewHandler(client, config, dm, pd).QueryView(ctx, *queryView)
if err != nil {
return nil, err
}

View File

@@ -17,10 +17,14 @@ limitations under the License.
package cli
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -39,6 +43,43 @@ import (
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
)
var _ = Describe("Test velaQL from file", func() {
It("Test Query pod data", func() {
cm := &corev1.ConfigMap{Data: map[string]string{"key": "my-value"}}
cm.Name = "mycm"
cm.Namespace = "default"
Expect(k8sClient.Create(context.TODO(), cm)).Should(BeNil())
view := `import (
"vela/ql"
)
configmap: ql.#Read & {
value: {
kind: "ConfigMap"
apiVersion: "v1"
metadata: {
name: "mycm"
}
}
}
status: configmap.value.data.key
export: "status"
`
name := "vela-test-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ".cue"
Expect(os.WriteFile(name, []byte(view), 0644)).Should(BeNil())
defer os.Remove(name)
arg := common2.Args{}
arg.SetConfig(cfg)
arg.SetClient(k8sClient)
cmd := NewCommand()
var buff = bytes.NewBufferString("")
cmd.SetOut(buff)
Expect(queryFromView(context.TODO(), k8sClient, arg, name, cmd)).Should(BeNil())
Expect(strings.TrimSpace(buff.String())).Should(BeEquivalentTo("my-value"))
})
})
var _ = Describe("Test velaQL", func() {
var appName = "test-velaql"
var namespace = "default"

View File

@@ -73,20 +73,38 @@ func GetNamespacedCapabilitiesFromCluster(ctx context.Context, namespace string,
capabilities = append(capabilities, traits...)
}
// get components from default namespace
if workloads, _, err := GetComponentsFromClusterWithValidateOption(ctx, types.DefaultKubeVelaNS, c, selector, false); err == nil {
capabilities = append(capabilities, workloads...)
if workflowSteps, _, err := GetWorkflowSteps(ctx, namespace, c); err == nil {
capabilities = append(capabilities, workflowSteps...)
}
// get traits from default namespace
if traits, _, err := GetTraitsFromClusterWithValidateOption(ctx, types.DefaultKubeVelaNS, c, selector, false); err == nil {
capabilities = append(capabilities, traits...)
if policies, _, err := GetPolicies(ctx, namespace, c); err == nil {
capabilities = append(capabilities, policies...)
}
if namespace != types.DefaultKubeVelaNS {
// get components from default namespace
if workloads, _, err := GetComponentsFromClusterWithValidateOption(ctx, types.DefaultKubeVelaNS, c, selector, false); err == nil {
capabilities = append(capabilities, workloads...)
}
// get traits from default namespace
if traits, _, err := GetTraitsFromClusterWithValidateOption(ctx, types.DefaultKubeVelaNS, c, selector, false); err == nil {
capabilities = append(capabilities, traits...)
}
if workflowSteps, _, err := GetWorkflowSteps(ctx, types.DefaultKubeVelaNS, c); err == nil {
capabilities = append(capabilities, workflowSteps...)
}
if policies, _, err := GetPolicies(ctx, types.DefaultKubeVelaNS, c); err == nil {
capabilities = append(capabilities, policies...)
}
}
if len(capabilities) > 0 {
return capabilities, nil
}
return nil, fmt.Errorf("could not find any components or traits from namespace %s and %s", namespace, types.DefaultKubeVelaNS)
return nil, fmt.Errorf("could not find any components, traits or workflowSteps from namespace %s and %s", namespace, types.DefaultKubeVelaNS)
}
// GetComponentsFromCluster will get capability from K8s cluster
@@ -184,6 +202,58 @@ func GetTraitsFromClusterWithValidateOption(ctx context.Context, namespace strin
return templates, templateErrors, nil
}
// GetWorkflowSteps will get WorkflowStepDefinition list
func GetWorkflowSteps(ctx context.Context, namespace string, c common.Args) ([]types.Capability, []error, error) {
newClient, err := c.GetClient()
if err != nil {
return nil, nil, err
}
var templates []types.Capability
var workflowStepDefs v1beta1.WorkflowStepDefinitionList
err = newClient.List(ctx, &workflowStepDefs, &client.ListOptions{Namespace: namespace})
if err != nil {
return nil, nil, fmt.Errorf("list WorkflowStepDefinition err: %w", err)
}
var templateErrors []error
for _, def := range workflowStepDefs.Items {
tmp, err := GetCapabilityByWorkflowStepDefinitionObject(def, nil)
if err != nil {
templateErrors = append(templateErrors, err)
continue
}
templates = append(templates, *tmp)
}
return templates, templateErrors, nil
}
// GetPolicies will get Policy from K8s cluster
func GetPolicies(ctx context.Context, namespace string, c common.Args) ([]types.Capability, []error, error) {
newClient, err := c.GetClient()
if err != nil {
return nil, nil, err
}
var templates []types.Capability
var defs v1beta1.PolicyDefinitionList
err = newClient.List(ctx, &defs, &client.ListOptions{Namespace: namespace})
if err != nil {
return nil, nil, fmt.Errorf("list PolicyDefinition err: %w", err)
}
var templateErrors []error
for _, def := range defs.Items {
tmp, err := GetCapabilityByPolicyDefinitionObject(def, nil)
if err != nil {
templateErrors = append(templateErrors, err)
continue
}
templates = append(templates, *tmp)
}
return templates, templateErrors, nil
}
// validateCapabilities validates whether helm charts are successful installed, GVK are successfully retrieved.
func validateCapabilities(tmp *types.Capability, dm discoverymapper.DiscoveryMapper, definitionName string, reference commontypes.DefinitionReference) error {
var err error
@@ -411,11 +481,7 @@ func GetCapabilityByTraitDefinitionObject(traitDef v1beta1.TraitDefinition) (*ty
// GetCapabilityByWorkflowStepDefinitionObject gets capability by WorkflowStepDefinition object
func GetCapabilityByWorkflowStepDefinitionObject(wfStepDef v1beta1.WorkflowStepDefinition, pd *packages.PackageDiscover) (*types.Capability, error) {
var (
capability types.Capability
err error
)
capability, err = HandleDefinition(wfStepDef.Name, wfStepDef.Spec.Reference.Name, wfStepDef.Annotations, wfStepDef.Labels,
capability, err := HandleDefinition(wfStepDef.Name, wfStepDef.Spec.Reference.Name, wfStepDef.Annotations, wfStepDef.Labels,
nil, types.TypeWorkflowStep, nil, wfStepDef.Spec.Schematic, pd)
if err != nil {
return nil, errors.Wrap(err, "failed to handle WorkflowStepDefinition")
@@ -423,3 +489,14 @@ func GetCapabilityByWorkflowStepDefinitionObject(wfStepDef v1beta1.WorkflowStepD
capability.Namespace = wfStepDef.Namespace
return &capability, nil
}
// GetCapabilityByPolicyDefinitionObject gets capability by PolicyDefinition object
func GetCapabilityByPolicyDefinitionObject(def v1beta1.PolicyDefinition, pd *packages.PackageDiscover) (*types.Capability, error) {
capability, err := HandleDefinition(def.Name, def.Spec.Reference.Name, def.Annotations, def.Labels,
nil, types.TypePolicy, nil, def.Spec.Schematic, pd)
if err != nil {
return nil, errors.Wrap(err, "failed to handle PolicyDefinition")
}
capability.Namespace = def.Namespace
return &capability, nil
}

View File

@@ -430,7 +430,8 @@ variable "acl" {
"configuration is in git remote": {
args: args{
cap: types.Capability{
TerraformConfiguration: "https://github.com/zzxwill/terraform-alibaba-eip.git",
Name: "ecs",
TerraformConfiguration: "https://github.com/wonderflow/terraform-alicloud-ecs-instance.git",
ConfigurationType: "remote",
},
},

View File

@@ -55,14 +55,6 @@ const (
KubeVelaIOTerraformPathZh = "../kubevela.io/i18n/zh/docusaurus-plugin-content-docs/current/end-user/components/cloud-services/terraform"
// ReferenceSourcePath is the location for source reference
ReferenceSourcePath = "hack/references"
// ComponentDefinitionTypePath is the URL path for component typed capability
ComponentDefinitionTypePath = "components"
// WorkloadTypePath is the URL path for workload typed capability
WorkloadTypePath = "workload-types"
// TraitPath is the URL path for trait typed capability
TraitPath = "traits"
// WorkflowStepPath is the URL path for workflow step typed capability
WorkflowStepPath = "workflowsteps"
)
// Int64Type is int64 type
@@ -656,18 +648,20 @@ func (ref *MarkdownReference) CreateMarkdown(ctx context.Context, caps []types.C
sample string
specification string
)
if c.Type != types.TypeWorkload && c.Type != types.TypeComponentDefinition && c.Type != types.TypeTrait {
if c.Type != types.TypeWorkload && c.Type != types.TypeComponentDefinition && c.Type != types.TypeTrait &&
c.Type != types.TypeWorkflowStep && c.Type != types.TypePolicy {
return fmt.Errorf("the type of the capability is not right")
}
fileName := fmt.Sprintf("%s.md", c.Name)
if _, err := os.Stat(baseRefPath); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(baseRefPath, 0750); err != nil {
refPath := filepath.Join(baseRefPath, string(c.Type))
if _, err := os.Stat(refPath); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(refPath, 0750); err != nil {
return err
}
}
markdownFile := filepath.Join(baseRefPath, fileName)
fileName := fmt.Sprintf("%s.md", c.Name)
markdownFile := filepath.Join(refPath, fileName)
f, err := os.OpenFile(filepath.Clean(markdownFile), os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return fmt.Errorf("failed to open file %s: %w", markdownFile, err)
@@ -869,11 +863,13 @@ func (ref *ParseReference) parseParameters(paraValue cue.Value, paramKey string,
}
if arguments.Len() == 0 {
var param ReferenceParameter
param.Name = "-"
param.Name = "\\-"
param.Required = true
tl := paraValue.Template()
if tl != nil { // is map type
param.PrintableType = fmt.Sprintf("map[string]%s", tl("").IncompleteKind().String())
} else {
param.PrintableType = "{}"
}
params = append(params, param)
}

Some files were not shown because too many files have changed in this diff Show More