Compare commits

...

54 Commits

Author SHA1 Message Date
Somefive
35a84e9cbf [Backport release-1.4] Fix: gc failure cause workflow restart not working properly (#5241)
* Fix: gc failure cause workflow restart not working properly

Signed-off-by: Somefive <yd219913@alibaba-inc.com>

* Feat: switch ci machine

Signed-off-by: Somefive <yd219913@alibaba-inc.com>

* Fix: enhance test

Signed-off-by: Somefive <yd219913@alibaba-inc.com>

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
2023-01-03 14:52:37 +08:00
github-actions[bot]
bf251d5039 Chore: change the package name of the readme-generator-for-helm (#4895)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 8b46e6076a)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-10-20 16:38:40 +08:00
github-actions[bot]
31f0b28d96 Fix: support default value of ui schema (#4858)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit e584a35c83)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-10-13 14:45:10 +08:00
github-actions[bot]
810c47545e [Backport release-1.4] Fix: allow to read definition from user's namespace when force delete (#4788)
* Fix: allow to read definition from user's namespace when force deleting app with configuration

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 2f08c36132)

* Fix test

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 981950a14d)

* Fix wrong test

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 62863f1007)

Co-authored-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-09-27 11:58:19 +08:00
github-actions[bot]
293f38dd84 Fix: panic when properties empty (#4747)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 13fec3cb18)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-09-19 11:18:33 +08:00
barnettZQG
1cf2cd23d2 Fix: CVE-2022-27664 (#4721) (#4724)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
2022-09-14 16:15:30 +08:00
github-actions[bot]
69cf083d4a Fix: fix uninstallation continues when answer is no (#4711)
Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit 81115ef6ff)

Co-authored-by: Charlie Chiang <charlie_c_0129@outlook.com>
2022-09-13 10:24:46 +08:00
Tianxin Dong
7df2b34a0b Fix: fix signedKey using platform id (#4653)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>

Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-08-24 19:33:24 +08:00
github-actions[bot]
2a03e16098 fix: add supported but missing provider (#4650)
Signed-off-by: Yuedong Wu <57584831+lunarwhite@users.noreply.github.com>
(cherry picked from commit fa96c917a8)

Co-authored-by: Yuedong Wu <57584831+lunarwhite@users.noreply.github.com>
2022-08-24 09:28:35 +08:00
github-actions[bot]
110927ed97 Fix: fix writing logs to file (#4588)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit d4b3bbf049)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-08-10 10:00:39 +08:00
github-actions[bot]
90fbfa0f81 Feat: definition support controller requirement (#4578)
Signed-off-by: yangsoon <songyang.song@alibaba-inc.com>
(cherry picked from commit 714f218f90)

Co-authored-by: yangsoon <songyang.song@alibaba-inc.com>
2022-08-08 16:07:44 +08:00
github-actions[bot]
7fb045328d [Backport release-1.4] Fix: reject applications with empty policy properties (#4565)
* Fix: reject applications with empty policies

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit 337032511e)

* Style: change err msg

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit 2bb5c0245a)

* Fix: use 400 instead of 422 to show err msg

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit 553ac92c62)

* Test: fix tests

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>
(cherry picked from commit 0ce352d13b)

Co-authored-by: Charlie Chiang <charlie_c_0129@outlook.com>
2022-08-05 15:04:47 +08:00
Somefive
766c5852c6 Fix: address vela-core crash due to empty policy properties (#4473) (#4480)
* Fix: fix topology core crash

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>

* Test: add tests

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>

* Fix: same problem in other places

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>

* Style: remove empty line

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>

* Feat: raise error when empty topology is used

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>

* Feat: raise error when empty override policy is used

Signed-off-by: Charlie Chiang <charlie_c_0129@outlook.com>

Co-authored-by: Charlie Chiang <charlie_c_0129@outlook.com>
2022-07-27 13:48:19 +08:00
github-actions[bot]
4cb9a14b18 Fix: fix logs to record the right publish version (#4476)
Signed-off-by: yangsoon <songyang.song@alibaba-inc.com>
(cherry picked from commit 4846104c8f)

Co-authored-by: yangsoon <songyang.song@alibaba-inc.com>
2022-07-27 01:13:07 +08:00
github-actions[bot]
9a1e75cf48 Fix: The apply failure error is ignored when the workflow is executed (#4461)
Signed-off-by: yangsoon <songyang.song@alibaba-inc.com>
(cherry picked from commit b1d8e6c88b)

Co-authored-by: yangsoon <songyang.song@alibaba-inc.com>
2022-07-25 22:19:26 +08:00
Tianxin Dong
192dc8966d Fix: fix backoff time after default backoff times (#4414)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-07-20 17:48:28 +08:00
Jianbo Sun
b596b70ebe Fix: addon function converted (#4411)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-07-19 19:54:44 +08:00
github-actions[bot]
0cd370e867 Fix: fix volumes duplicate in list (#4390)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 08fb73aa95)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-07-15 20:11:49 +08:00
github-actions[bot]
d9adc73e5c Fix: add usage comment for ref-objects (#4386)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 0418c83117)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-07-14 18:51:05 +08:00
github-actions[bot]
4a2d9807c8 [Backport release-1.4] Fix: fail directly when app terminated (#4385)
* fail directly when app terminated

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

* support suspend

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

fix typo

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-07-14 18:50:23 +08:00
github-actions[bot]
840cb8ce58 Fix: several minor bugs (#4381)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit c5f10a5723)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-07-14 11:22:41 +08:00
github-actions[bot]
5a64fec916 Fix: abuse timeout context in terraform provider (#4375)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit ab56b3c274)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-07-13 15:48:13 +08:00
Jianbo Sun
657a374ded Fix: add the job of independently publishing chart packages (#4360) (#4361)
* Fix: add the job of independently publishing chart packages

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>

* Fix: add the job of independently publishing chart packages

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-07-12 12:31:05 +08:00
Tianxin Dong
dfe12cd9ca [Backport-1.4]: optimize imports packages to reduce 75% cpu with better performance (#4355)
* Feat: optimize imports packages

Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>

* fix test

Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-07-11 18:33:53 +08:00
github-actions[bot]
cd42f67848 Fix: init container bug (#4354)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 3f116c7f10)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-07-11 17:03:49 +08:00
github-actions[bot]
61d2c588e3 Fix: health check use original ns if no override and original exists (#4353)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit f7923b5ac9)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-07-11 16:49:24 +08:00
github-actions[bot]
b3dad698a5 Fix: enhance sidecar & init traits (#4343)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit dc9b18d119)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-07-08 19:09:55 +08:00
github-actions[bot]
ec5159c2ca Fix: disable apprev status update when apprev disabled (#4338)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit b4c8e3265a)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-07-07 15:51:35 +08:00
github-actions[bot]
a7b2b221e0 [Backport release-1.4] Fix: more cluster system info range. (#4333)
* more collect info

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

* fix comments

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-07-06 16:15:31 +08:00
github-actions[bot]
caa495a5d9 [Backport release-1.4] Fix: ref-objects parameter with invalid field definition (#4330)
* fix: ref-objects parameter with invalid field definition

which cause validating webhook failed when use ref-objects component

Signed-off-by: jiangshantao <jiangshantao-dbg@qq.com>
(cherry picked from commit 13f328f362)

* fix: run make reviewable

Signed-off-by: jiangshantao <jiangshantao-dbg@qq.com>
(cherry picked from commit e09410af90)

Co-authored-by: jst <jst@meitu.com>
2022-07-06 14:18:17 +08:00
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
164 changed files with 2762 additions and 1323 deletions

View File

@@ -55,7 +55,7 @@ jobs:
apiserver-unit-tests:
runs-on: aliyun
runs-on: aliyun-legacy
needs: [ detect-noop,set-k8s-matrix ]
if: needs.detect-noop.outputs.noop != 'true'
strategy:

89
.github/workflows/chart.yaml vendored Normal file
View File

@@ -0,0 +1,89 @@
name: Publish Chart
on:
push:
tags:
- "v*"
workflow_dispatch: { }
env:
BUCKET: ${{ secrets.OSS_BUCKET }}
ENDPOINT: ${{ secrets.OSS_ENDPOINT }}
ACCESS_KEY: ${{ secrets.OSS_ACCESS_KEY }}
ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
ARTIFACT_HUB_REPOSITORY_ID: ${{ secrets.ARTIFACT_HUB_REPOSITORY_ID }}
jobs:
publish-charts:
env:
HELM_CHARTS_DIR: charts
HELM_CHART: charts/vela-core
MINIMAL_HELM_CHART: charts/vela-minimal
LEGACY_HELM_CHART: legacy/charts/vela-core-legacy
VELA_ROLLOUT_HELM_CHART: runtime/rollout/charts
LOCAL_OSS_DIRECTORY: .oss/
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@master
- name: Get git revision
id: vars
shell: bash
run: |
echo "::set-output name=git_revision::$(git rev-parse --short HEAD)"
- name: Install Helm
uses: azure/setup-helm@v1
with:
version: v3.4.0
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Generate helm doc
run: |
make helm-doc-gen
- name: Prepare legacy chart
run: |
rsync -r $LEGACY_HELM_CHART $HELM_CHARTS_DIR
rsync -r $HELM_CHART/* $LEGACY_HELM_CHART --exclude=Chart.yaml --exclude=crds
- name: Prepare vela chart
run: |
rsync -r $VELA_ROLLOUT_HELM_CHART $HELM_CHARTS_DIR
- name: Get the version
id: get_version
run: |
VERSION=${GITHUB_REF#refs/tags/}
echo ::set-output name=VERSION::${VERSION}
- name: Tag helm chart image
run: |
image_tag=${{ steps.get_version.outputs.VERSION }}
chart_version=${{ steps.get_version.outputs.VERSION }}
sed -i "s/latest/${image_tag}/g" $HELM_CHART/values.yaml
sed -i "s/latest/${image_tag}/g" $MINIMAL_HELM_CHART/values.yaml
sed -i "s/latest/${image_tag}/g" $LEGACY_HELM_CHART/values.yaml
sed -i "s/latest/${image_tag}/g" $VELA_ROLLOUT_HELM_CHART/values.yaml
chart_smever=${chart_version#"v"}
sed -i "s/0.1.0/$chart_smever/g" $HELM_CHART/Chart.yaml
sed -i "s/0.1.0/$chart_smever/g" $MINIMAL_HELM_CHART/Chart.yaml
sed -i "s/0.1.0/$chart_smever/g" $LEGACY_HELM_CHART/Chart.yaml
sed -i "s/0.1.0/$chart_smever/g" $VELA_ROLLOUT_HELM_CHART/Chart.yaml
- name: Install ossutil
run: wget http://gosspublic.alicdn.com/ossutil/1.7.0/ossutil64 && chmod +x ossutil64 && mv ossutil64 ossutil
- name: Configure Alibaba Cloud OSSUTIL
run: ./ossutil --config-file .ossutilconfig config -i ${ACCESS_KEY} -k ${ACCESS_KEY_SECRET} -e ${ENDPOINT} -c .ossutilconfig
- name: sync cloud to local
run: ./ossutil --config-file .ossutilconfig sync oss://$BUCKET/core $LOCAL_OSS_DIRECTORY
- name: add artifacthub stuff to the repo
run: |
rsync $HELM_CHART/README.md $LEGACY_HELM_CHART/README.md
rsync $HELM_CHART/README.md $VELA_ROLLOUT_HELM_CHART/README.md
sed -i "s/ARTIFACT_HUB_REPOSITORY_ID/$ARTIFACT_HUB_REPOSITORY_ID/g" hack/artifacthub/artifacthub-repo.yml
rsync hack/artifacthub/artifacthub-repo.yml $LOCAL_OSS_DIRECTORY
- name: Package helm charts
run: |
helm package $HELM_CHART --destination $LOCAL_OSS_DIRECTORY
helm package $MINIMAL_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
helm package $LEGACY_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
helm package $VELA_ROLLOUT_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
helm repo index --url https://$BUCKET.$ENDPOINT/core $LOCAL_OSS_DIRECTORY
- name: sync local to cloud
run: ./ossutil --config-file .ossutilconfig sync $LOCAL_OSS_DIRECTORY oss://$BUCKET/core -f

View File

@@ -53,7 +53,7 @@ jobs:
e2e-multi-cluster-tests:
runs-on: aliyun
runs-on: aliyun-legacy
needs: [ detect-noop,set-k8s-matrix ]
if: needs.detect-noop.outputs.noop != 'true'
strategy:
@@ -97,7 +97,9 @@ jobs:
kubectl cluster-info
- name: Load Image to kind cluster (Hub)
run: make kind-load
run: |
make kind-load
make kind-load-runtime-cluster
- name: Cleanup for e2e tests
run: |

View File

@@ -52,7 +52,7 @@ jobs:
fi
e2e-rollout-tests:
runs-on: aliyun
runs-on: aliyun-legacy
needs: [ detect-noop,set-k8s-matrix ]
if: needs.detect-noop.outputs.noop != 'true'
strategy:

View File

@@ -52,7 +52,7 @@ jobs:
fi
e2e-tests:
runs-on: aliyun
runs-on: aliyun-legacy
needs: [ detect-noop,set-k8s-matrix ]
if: needs.detect-noop.outputs.noop != 'true'
strategy:

View File

@@ -98,7 +98,7 @@ jobs:
version: ${{ env.GOLANGCI_VERSION }}
check-diff:
runs-on: aliyun
runs-on: aliyun-legacy
needs: detect-noop
if: needs.detect-noop.outputs.noop != 'true'

View File

@@ -8,11 +8,8 @@ on:
workflow_dispatch: {}
env:
BUCKET: ${{ secrets.OSS_BUCKET }}
ENDPOINT: ${{ secrets.OSS_ENDPOINT }}
ACCESS_KEY: ${{ secrets.OSS_ACCESS_KEY }}
ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
ARTIFACT_HUB_REPOSITORY_ID: ${{ secrets.ARTIFACT_HUB_REPOSITORY_ID }}
jobs:
publish-core-images:
@@ -47,8 +44,8 @@ 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
@@ -72,7 +69,7 @@ jobs:
tags: |-
docker.io/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
${{ secrets.ACR_DOMAIN }}/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
- uses: docker/build-push-action@v2
name: Build & Pushing CLI for Dockerhub, GHCR and ACR
@@ -91,7 +88,7 @@ jobs:
tags: |-
docker.io/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
kubevela-registry.cn-hangzhou.cr.aliyuncs.com/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
@@ -125,8 +122,8 @@ 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
@@ -150,7 +147,7 @@ jobs:
tags: |-
docker.io/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
kubevela-registry.cn-hangzhou.cr.aliyuncs.com/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
@@ -169,91 +166,7 @@ jobs:
tags: |-
docker.io/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
publish-charts:
env:
HELM_CHARTS_DIR: charts
HELM_CHART: charts/vela-core
MINIMAL_HELM_CHART: charts/vela-minimal
LEGACY_HELM_CHART: legacy/charts/vela-core-legacy
VELA_ROLLOUT_HELM_CHART: runtime/rollout/charts
LOCAL_OSS_DIRECTORY: .oss/
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@master
- name: Get git revision
id: vars
shell: bash
run: |
echo "::set-output name=git_revision::$(git rev-parse --short HEAD)"
- name: Install Helm
uses: azure/setup-helm@v1
with:
version: v3.4.0
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Generate helm doc
run: |
make helm-doc-gen
- name: Prepare legacy chart
run: |
rsync -r $LEGACY_HELM_CHART $HELM_CHARTS_DIR
rsync -r $HELM_CHART/* $LEGACY_HELM_CHART --exclude=Chart.yaml --exclude=crds
- name: Prepare vela chart
run: |
rsync -r $VELA_ROLLOUT_HELM_CHART $HELM_CHARTS_DIR
- uses: oprypin/find-latest-tag@v1
with:
repository: oam-dev/kubevela
releases-only: true
id: latest_tag
- name: Tag helm chart image
run: |
latest_repo_tag=${{ steps.latest_tag.outputs.tag }}
sub="."
major="$(cut -d"$sub" -f1 <<<"$latest_repo_tag")"
minor="$(cut -d"$sub" -f2 <<<"$latest_repo_tag")"
patch="0"
current_repo_tag="$major.$minor.$patch"
image_tag=${GITHUB_REF#refs/tags/}
chart_version=$latest_repo_tag
if [[ ${GITHUB_REF} == "refs/heads/master" ]]; then
image_tag=latest
chart_version=${current_repo_tag}-nightly-build
fi
sed -i "s/latest/${image_tag}/g" $HELM_CHART/values.yaml
sed -i "s/latest/${image_tag}/g" $MINIMAL_HELM_CHART/values.yaml
sed -i "s/latest/${image_tag}/g" $LEGACY_HELM_CHART/values.yaml
sed -i "s/latest/${image_tag}/g" $VELA_ROLLOUT_HELM_CHART/values.yaml
chart_smever=${chart_version#"v"}
sed -i "s/0.1.0/$chart_smever/g" $HELM_CHART/Chart.yaml
sed -i "s/0.1.0/$chart_smever/g" $MINIMAL_HELM_CHART/Chart.yaml
sed -i "s/0.1.0/$chart_smever/g" $LEGACY_HELM_CHART/Chart.yaml
sed -i "s/0.1.0/$chart_smever/g" $VELA_ROLLOUT_HELM_CHART/Chart.yaml
- name: Install ossutil
run: wget http://gosspublic.alicdn.com/ossutil/1.7.0/ossutil64 && chmod +x ossutil64 && mv ossutil64 ossutil
- name: Configure Alibaba Cloud OSSUTIL
run: ./ossutil --config-file .ossutilconfig config -i ${ACCESS_KEY} -k ${ACCESS_KEY_SECRET} -e ${ENDPOINT} -c .ossutilconfig
- name: sync cloud to local
run: ./ossutil --config-file .ossutilconfig sync oss://$BUCKET/core $LOCAL_OSS_DIRECTORY
- name: add artifacthub stuff to the repo
run: |
rsync $HELM_CHART/README.md $LEGACY_HELM_CHART/README.md
rsync $HELM_CHART/README.md $VELA_ROLLOUT_HELM_CHART/README.md
sed -i "s/ARTIFACT_HUB_REPOSITORY_ID/$ARTIFACT_HUB_REPOSITORY_ID/g" hack/artifacthub/artifacthub-repo.yml
rsync hack/artifacthub/artifacthub-repo.yml $LOCAL_OSS_DIRECTORY
- name: Package helm charts
run: |
helm package $HELM_CHART --destination $LOCAL_OSS_DIRECTORY
helm package $MINIMAL_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
helm package $LEGACY_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
helm package $VELA_ROLLOUT_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
helm repo index --url https://$BUCKET.$ENDPOINT/core $LOCAL_OSS_DIRECTORY
- name: sync local to cloud
run: ./ossutil --config-file .ossutilconfig sync $LOCAL_OSS_DIRECTORY oss://$BUCKET/core -f
${{ secrets.ACR_DOMAIN }}/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
publish-capabilities:
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

@@ -4,7 +4,7 @@ on:
- cron: '* * * * *'
jobs:
clean-image:
runs-on: aliyun
runs-on: aliyun-legacy
steps:
- name: Cleanup image
run: docker image prune -f

View File

@@ -83,15 +83,17 @@ endif
# load docker image to the kind cluster
kind-load: kind-load-runtime-cluster
kind-load: kind-load-rollout
docker build -t $(VELA_CORE_TEST_IMAGE) -f Dockerfile.e2e .
kind load docker-image $(VELA_CORE_TEST_IMAGE) || { echo >&2 "kind not installed or error loading image: $(VELA_CORE_TEST_IMAGE)"; exit 1; }
kind-load-runtime-cluster:
kind-load-rollout:
/bin/sh hack/e2e/build_runtime_rollout.sh
docker build -t $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) -f runtime/rollout/e2e/Dockerfile.e2e runtime/rollout/e2e/
rm -rf runtime/rollout/e2e/tmp
kind load docker-image $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) || { echo >&2 "kind not installed or error loading image: $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE)"; exit 1; }
kind-load-runtime-cluster:
kind load docker-image $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) --name=$(RUNTIME_CLUSTER_NAME) || { echo >&2 "kind not installed or error loading image: $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE)"; exit 1; }
# Run tests

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

@@ -43,7 +43,7 @@ spec:
volumeMounts: [{
name: parameter.mountName
mountPath: parameter.initMountPath
}]
}] + parameter.extraVolumeMounts
}]
// +patchKey=name
volumes: [{
@@ -97,5 +97,13 @@ spec:
// +usage=Specify the mount path of init container
initMountPath: string
// +usage=Specify the extra volume mounts for the init container
extraVolumeMounts: [...{
// +usage=The name of the volume to be mounted
name: string
// +usage=The mountPath for mount in the init container
mountPath: string
}]
}

View File

@@ -14,12 +14,18 @@ spec:
cue:
template: |
#K8sObject: {
apiVersion: string
kind: string
metadata: {
name: string
...
}
// +usage=The resource type for the Kubernetes objects
resource?: string
// +usage=The group name for the Kubernetes objects
group?: string
// +usage=If specified, fetch the Kubernetes objects with the name, exclusive to labelSelector
name?: string
// +usage=If specified, fetch the Kubernetes objects from the namespace. Otherwise, fetch from the application's namespace.
namespace?: string
// +usage=If specified, fetch the Kubernetes objects from the cluster. Otherwise, fetch from the local cluster.
cluster?: string
// +usage=If specified, fetch the Kubernetes objects according to the label selector, exclusive to name
labelSelector?: [string]: string
...
}
output: parameter.objects[0]
@@ -30,7 +36,12 @@ spec:
}
}
}
parameter: objects: [...#K8sObject]
parameter: {
// +usage=If specified, application will fetch native Kubernetes objects according to the object description
objects?: [...#K8sObject]
// +usage=If specified, the objects in the urls will be loaded.
urls?: [...string]
}
status:
customStatus: |-
if context.output.apiVersion == "apps/v1" && context.output.kind == "Deployment" {

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 != _|_ {
resourceNames: 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 != _|_ {
resourceNames: 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

@@ -82,6 +82,11 @@ spec:
// +usage=The key of the config map to select from. Must be a valid secret key
key: string
}
// +usage=Specify the field reference for env
fieldRef?: {
// +usage=Specify the field path for env
fieldPath: string
}
}
}]

View File

@@ -64,6 +64,9 @@ spec:
{
name: "pvc-" + v.name
mountPath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
}
},
@@ -73,6 +76,9 @@ spec:
{
name: "configmap-" + v.name
mountPath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
},
] | []
@@ -103,6 +109,9 @@ spec:
{
name: "secret-" + v.name
mountPath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
},
] | []
@@ -133,6 +142,9 @@ spec:
{
name: "emptydir-" + v.name
mountPath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
},
] | []
@@ -141,12 +153,28 @@ spec:
{
name: "pvc-" + v.name
devicePath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
},
] | []
volumesList: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
deDupVolumesArray: [
for val in [
for i, vi in volumesList {
for j, vj in volumesList if j < i && vi.name == vj.name {
_ignore: true
}
vi
},
] if val._ignore == _|_ {
val
},
]
patch: spec: template: spec: {
// +patchKey=name
volumes: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
volumes: deDupVolumesArray
containers: [{
// +patchKey=name
@@ -234,6 +262,7 @@ spec:
name: string
mountOnly: *false | bool
mountPath: string
subPath?: string
volumeMode: *"Filesystem" | string
volumeName?: string
accessModes: *["ReadWriteOnce"] | [...string]
@@ -275,6 +304,7 @@ spec:
configMapKey: string
}]
mountPath?: string
subPath?: string
defaultMode: *420 | int
readOnly: *false | bool
data?: {...}
@@ -298,6 +328,7 @@ spec:
secretKey: string
}]
mountPath?: string
subPath?: string
defaultMode: *420 | int
readOnly: *false | bool
stringData?: {...}
@@ -313,6 +344,7 @@ spec:
emptyDir?: [...{
name: string
mountPath: string
subPath?: string
medium: *"" | "Memory"
}]
}

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

@@ -20,7 +20,10 @@ spec:
for v in parameter.volumeMounts.pvc {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -29,7 +32,10 @@ spec:
for v in parameter.volumeMounts.configMap {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -38,7 +44,10 @@ spec:
for v in parameter.volumeMounts.secret {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -47,7 +56,10 @@ spec:
for v in parameter.volumeMounts.emptyDir {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -56,7 +68,10 @@ spec:
for v in parameter.volumeMounts.hostPath {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -119,6 +134,19 @@ spec:
},
] | []
}
volumesList: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
deDupVolumesArray: [
for val in [
for i, vi in volumesList {
for j, vj in volumesList if j < i && vi.name == vj.name {
_ignore: true
}
vi
},
] if val._ignore == _|_ {
val
},
]
output: {
apiVersion: "apps/v1"
kind: "Deployment"
@@ -262,7 +290,7 @@ spec:
}
if parameter["volumeMounts"] != _|_ {
volumes: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
volumes: deDupVolumesArray
}
}
}
@@ -375,6 +403,7 @@ spec:
pvc?: [...{
name: string
mountPath: string
subPath?: string
// +usage=The name of the PVC
claimName: string
}]
@@ -382,6 +411,7 @@ spec:
configMap?: [...{
name: string
mountPath: string
subPath?: string
defaultMode: *420 | int
cmName: string
items?: [...{
@@ -394,6 +424,7 @@ spec:
secret?: [...{
name: string
mountPath: string
subPath?: string
defaultMode: *420 | int
secretName: string
items?: [...{
@@ -406,12 +437,14 @@ spec:
emptyDir?: [...{
name: string
mountPath: string
subPath?: string
medium: *"" | "Memory"
}]
// +usage=Mount HostPath type volume
hostPath?: [...{
name: string
mountPath: string
subPath?: string
path: string
}]
}

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

@@ -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

@@ -43,7 +43,7 @@ spec:
volumeMounts: [{
name: parameter.mountName
mountPath: parameter.initMountPath
}]
}] + parameter.extraVolumeMounts
}]
// +patchKey=name
volumes: [{
@@ -97,5 +97,13 @@ spec:
// +usage=Specify the mount path of init container
initMountPath: string
// +usage=Specify the extra volume mounts for the init container
extraVolumeMounts: [...{
// +usage=The name of the volume to be mounted
name: string
// +usage=The mountPath for mount in the init container
mountPath: string
}]
}

View File

@@ -14,12 +14,18 @@ spec:
cue:
template: |
#K8sObject: {
apiVersion: string
kind: string
metadata: {
name: string
...
}
// +usage=The resource type for the Kubernetes objects
resource?: string
// +usage=The group name for the Kubernetes objects
group?: string
// +usage=If specified, fetch the Kubernetes objects with the name, exclusive to labelSelector
name?: string
// +usage=If specified, fetch the Kubernetes objects from the namespace. Otherwise, fetch from the application's namespace.
namespace?: string
// +usage=If specified, fetch the Kubernetes objects from the cluster. Otherwise, fetch from the local cluster.
cluster?: string
// +usage=If specified, fetch the Kubernetes objects according to the label selector, exclusive to name
labelSelector?: [string]: string
...
}
output: parameter.objects[0]
@@ -30,7 +36,12 @@ spec:
}
}
}
parameter: objects: [...#K8sObject]
parameter: {
// +usage=If specified, application will fetch native Kubernetes objects according to the object description
objects?: [...#K8sObject]
// +usage=If specified, the objects in the urls will be loaded.
urls?: [...string]
}
status:
customStatus: |-
if context.output.apiVersion == "apps/v1" && context.output.kind == "Deployment" {

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 != _|_ {
resourceNames: 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 != _|_ {
resourceNames: 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

@@ -82,6 +82,11 @@ spec:
// +usage=The key of the config map to select from. Must be a valid secret key
key: string
}
// +usage=Specify the field reference for env
fieldRef?: {
// +usage=Specify the field path for env
fieldPath: string
}
}
}]

View File

@@ -64,6 +64,9 @@ spec:
{
name: "pvc-" + v.name
mountPath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
}
},
@@ -73,6 +76,9 @@ spec:
{
name: "configmap-" + v.name
mountPath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
},
] | []
@@ -103,6 +109,9 @@ spec:
{
name: "secret-" + v.name
mountPath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
},
] | []
@@ -133,6 +142,9 @@ spec:
{
name: "emptydir-" + v.name
mountPath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
},
] | []
@@ -141,12 +153,28 @@ spec:
{
name: "pvc-" + v.name
devicePath: v.mountPath
if v.subPath != _|_ {
subPath: v.subPath
}
}
},
] | []
volumesList: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
deDupVolumesArray: [
for val in [
for i, vi in volumesList {
for j, vj in volumesList if j < i && vi.name == vj.name {
_ignore: true
}
vi
},
] if val._ignore == _|_ {
val
},
]
patch: spec: template: spec: {
// +patchKey=name
volumes: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
volumes: deDupVolumesArray
containers: [{
// +patchKey=name
@@ -234,6 +262,7 @@ spec:
name: string
mountOnly: *false | bool
mountPath: string
subPath?: string
volumeMode: *"Filesystem" | string
volumeName?: string
accessModes: *["ReadWriteOnce"] | [...string]
@@ -275,6 +304,7 @@ spec:
configMapKey: string
}]
mountPath?: string
subPath?: string
defaultMode: *420 | int
readOnly: *false | bool
data?: {...}
@@ -298,6 +328,7 @@ spec:
secretKey: string
}]
mountPath?: string
subPath?: string
defaultMode: *420 | int
readOnly: *false | bool
stringData?: {...}
@@ -313,6 +344,7 @@ spec:
emptyDir?: [...{
name: string
mountPath: string
subPath?: string
medium: *"" | "Memory"
}]
}

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

@@ -20,7 +20,10 @@ spec:
for v in parameter.volumeMounts.pvc {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -29,7 +32,10 @@ spec:
for v in parameter.volumeMounts.configMap {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -38,7 +44,10 @@ spec:
for v in parameter.volumeMounts.secret {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -47,7 +56,10 @@ spec:
for v in parameter.volumeMounts.emptyDir {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -56,7 +68,10 @@ spec:
for v in parameter.volumeMounts.hostPath {
{
mountPath: v.mountPath
name: v.name
if v.subPath != _|_ {
subPath: v.subPath
}
name: v.name
}
},
] | []
@@ -119,6 +134,19 @@ spec:
},
] | []
}
volumesList: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
deDupVolumesArray: [
for val in [
for i, vi in volumesList {
for j, vj in volumesList if j < i && vi.name == vj.name {
_ignore: true
}
vi
},
] if val._ignore == _|_ {
val
},
]
output: {
apiVersion: "apps/v1"
kind: "Deployment"
@@ -262,7 +290,7 @@ spec:
}
if parameter["volumeMounts"] != _|_ {
volumes: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
volumes: deDupVolumesArray
}
}
}
@@ -375,6 +403,7 @@ spec:
pvc?: [...{
name: string
mountPath: string
subPath?: string
// +usage=The name of the PVC
claimName: string
}]
@@ -382,6 +411,7 @@ spec:
configMap?: [...{
name: string
mountPath: string
subPath?: string
defaultMode: *420 | int
cmName: string
items?: [...{
@@ -394,6 +424,7 @@ spec:
secret?: [...{
name: string
mountPath: string
subPath?: string
defaultMode: *420 | int
secretName: string
items?: [...{
@@ -406,12 +437,14 @@ spec:
emptyDir?: [...{
name: string
mountPath: string
subPath?: string
medium: *"" | "Memory"
}]
// +usage=Mount HostPath type volume
hostPath?: [...{
name: string
mountPath: string
subPath?: string
path: string
}]
}

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

@@ -19,6 +19,7 @@ package main
import (
"context"
"errors"
goflag "flag"
"fmt"
"io"
"net/http"
@@ -138,6 +139,7 @@ func main() {
flag.DurationVar(&clusterMetricsInterval, "cluster-metrics-interval", 15*time.Second, "The interval that ClusterMetricsMgr will collect metrics from clusters, default value is 15 seconds.")
flag.BoolVar(&controllerArgs.EnableCompatibility, "enable-asi-compatibility", false, "enable compatibility for asi")
flag.BoolVar(&controllerArgs.IgnoreAppWithoutControllerRequirement, "ignore-app-without-controller-version", false, "If true, application controller will not process the app without 'app.oam.dev/controller-version-require' annotation")
flag.BoolVar(&controllerArgs.IgnoreDefinitionWithoutControllerRequirement, "ignore-definition-without-controller-version", false, "If true, trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation")
standardcontroller.AddOptimizeFlags()
standardcontroller.AddAdmissionFlags()
flag.IntVar(&resourcekeeper.MaxDispatchConcurrent, "max-dispatch-concurrent", 10, "Set the max dispatch concurrent number, default is 10")
@@ -146,9 +148,10 @@ func main() {
flag.IntVar(&custom.MaxWorkflowStepErrorRetryTimes, "max-workflow-step-error-retry-times", 10, "Set the max workflow step error retry times, default is 10")
utilfeature.DefaultMutableFeatureGate.AddFlag(flag.CommandLine)
flag.Parse()
// setup logging
klog.InitFlags(nil)
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
flag.Parse()
if logDebug {
_ = flag.Set("v", strconv.Itoa(int(commonconfig.LogDebug)))
}

View File

@@ -25,3 +25,7 @@ spec:
- name: my-mount
mountPath: /test
claimName: myclaim
- name: my-mount
mountPath: /test2
subPath: /sub
claimName: myclaim

View File

@@ -16,8 +16,9 @@ spec:
pvc:
- name: test1
mountPath: /test/mount/pvc
- name: test2
- name: test1
mountPath: /test/mount2/pvc
subPath: /sub
configMap:
- name: test1
mountPath: /test/mount/cm

6
go.mod
View File

@@ -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
@@ -281,9 +281,9 @@ require (
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220516155154-20f960328961 // indirect
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect

10
go.sum
View File

@@ -1344,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=
@@ -2371,8 +2371,9 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220516155154-20f960328961 h1:+W/iTMPG0EL7aW+/atntZwZrvSRIj3m3yX414dSULUU=
golang.org/x/net v0.0.0-20220516155154-20f960328961/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -2557,8 +2558,9 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY=
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

View File

@@ -77,7 +77,7 @@ ifeq (, $(shell which readme-generator))
@{ \
set -e ;\
echo 'installing readme-generator-for-helm' ;\
npm install -g readme-generator-for-helm ;\
npm install -g @bitnami/readme-generator-for-helm ;\
}
else
@$(OK) readme-generator-for-helm is already installed

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

@@ -86,7 +86,6 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Expect(res).To(HaveLen(1))
Expect(res[0].Name).To(Equal("velaux"))
Expect(res[0].InstallPackage).ToNot(BeNil())
Expect(res[0].APISchema).ToNot(BeNil())
})
It("should return one valid result, matching one registry", func() {
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"velaux"}, []string{"KubeVela"})
@@ -94,7 +93,6 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Expect(res).To(HaveLen(1))
Expect(res[0].Name).To(Equal("velaux"))
Expect(res[0].InstallPackage).ToNot(BeNil())
Expect(res[0].APISchema).ToNot(BeNil())
})
})
@@ -113,10 +111,8 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Expect(res).To(HaveLen(2))
Expect(res[0].Name).To(Equal("velaux"))
Expect(res[0].InstallPackage).ToNot(BeNil())
Expect(res[0].APISchema).ToNot(BeNil())
Expect(res[1].Name).To(Equal("traefik"))
Expect(res[1].InstallPackage).ToNot(BeNil())
Expect(res[1].APISchema).ToNot(BeNil())
})
})
@@ -127,7 +123,6 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
Expect(res).To(HaveLen(1))
Expect(res[0].Name).To(Equal("velaux"))
Expect(res[0].InstallPackage).ToNot(BeNil())
Expect(res[0].APISchema).ToNot(BeNil())
})
})
})

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

@@ -32,6 +32,7 @@ const (
// SystemInfo systemInfo model
type SystemInfo struct {
BaseModel
SignedKey string `json:"signedKey"`
InstallID string `json:"installID"`
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType"`

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

@@ -57,7 +57,8 @@ const (
GrantTypeRefresh = "refresh"
)
var signedKey = ""
// signedKey is the signed key of JWT
var signedKey string
// AuthenticationService is the service of authentication
type AuthenticationService interface {

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

@@ -52,6 +52,9 @@ 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"`
}
@@ -344,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

@@ -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

@@ -63,6 +63,7 @@ func (u systemInfoServiceImpl) Get(ctx context.Context) (*model.SystemInfo, erro
}
return info, nil
}
info.SignedKey = rand.String(32)
installID := rand.String(16)
info.InstallID = installID
info.EnableCollection = true
@@ -159,7 +160,7 @@ func (u systemInfoServiceImpl) Init(ctx context.Context) error {
if err != nil {
return err
}
signedKey = info.InstallID
signedKey = info.SignedKey
_, err = initDexConfig(ctx, u.KubeClient, "http://velaux.com")
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

@@ -322,7 +322,17 @@ func genClusterCountInfo(num int) string {
return "<10"
case num < 50:
return "<50"
case num < 100:
return "<100"
case num < 150:
return "<150"
case num < 200:
return "<200"
case num < 300:
return "<300"
case num < 500:
return "<500"
default:
return ">=50"
return ">=500"
}
}

View File

@@ -211,8 +211,28 @@ func TestGenClusterCountInfo(t *testing.T) {
res: "<50",
},
{
count: 100,
res: ">=50",
count: 90,
res: "<100",
},
{
count: 137,
res: "<150",
},
{
count: 170,
res: "<200",
},
{
count: 270,
res: "<300",
},
{
count: 400,
res: "<500",
},
{
count: 520,
res: ">=500",
},
}
for _, testcase := range testcases {

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

@@ -82,8 +82,8 @@ func (c Condition) Validate() error {
if c.JSONKey == "" {
return fmt.Errorf("the json key of the condition can not be empty")
}
if c.Action != "enable" && c.Action != "disable" {
return fmt.Errorf("the action of the condition must be enable or disable")
if c.Action != "enable" && c.Action != "disable" && c.Action != "" {
return fmt.Errorf("the action of the condition only supports enable, disable or leave it empty")
}
if c.Op != "" && !StringsContain([]string{"==", "!=", "in"}, c.Op) {
return fmt.Errorf("the op of the condition must be `==` 、`!=` and `in`")

View File

@@ -97,21 +97,21 @@ func (wl *Workload) EvalContext(ctx process.Context) error {
}
// EvalStatus eval workload status
func (wl *Workload) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) {
func (wl *Workload) EvalStatus(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor) (string, error) {
// if the standard workload is managed by trait always return empty message
if wl.SkipApplyWorkload {
return "", nil
}
return wl.engine.Status(ctx, cli, ns, wl.FullTemplate.CustomStatus, wl.Params)
return wl.engine.Status(ctx, cli, accessor, wl.FullTemplate.CustomStatus, wl.Params)
}
// EvalHealth eval workload health check
func (wl *Workload) EvalHealth(ctx process.Context, client client.Client, namespace string) (bool, error) {
func (wl *Workload) EvalHealth(ctx process.Context, client client.Client, accessor util.NamespaceAccessor) (bool, error) {
// if health of template is not set or standard workload is managed by trait always return true
if wl.FullTemplate.Health == "" || wl.SkipApplyWorkload {
return true, nil
}
return wl.engine.HealthCheck(ctx, client, namespace, wl.FullTemplate.Health)
return wl.engine.HealthCheck(ctx, client, accessor, wl.FullTemplate.Health)
}
// Scope defines the scope of workload
@@ -145,16 +145,16 @@ func (trait *Trait) EvalContext(ctx process.Context) error {
}
// EvalStatus eval trait status
func (trait *Trait) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) {
return trait.engine.Status(ctx, cli, ns, trait.CustomStatusFormat, trait.Params)
func (trait *Trait) EvalStatus(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor) (string, error) {
return trait.engine.Status(ctx, cli, accessor, trait.CustomStatusFormat, trait.Params)
}
// EvalHealth eval trait health check
func (trait *Trait) EvalHealth(ctx process.Context, client client.Client, namespace string) (bool, error) {
func (trait *Trait) EvalHealth(ctx process.Context, client client.Client, accessor util.NamespaceAccessor) (bool, error) {
if trait.FullTemplate.Health == "" {
return true, nil
}
return trait.engine.HealthCheck(ctx, client, namespace, trait.HealthCheckPolicy)
return trait.engine.HealthCheck(ctx, client, accessor, trait.HealthCheckPolicy)
}
// Appfile describes application

View File

@@ -365,6 +365,9 @@ func (p *Parser) parsePoliciesFromRevision(ctx context.Context, af *Appfile) (er
return err
}
for _, policy := range af.Policies {
if policy.Properties == nil && policy.Type != v1alpha1.DebugPolicyType {
return fmt.Errorf("policy %s named %s must not have empty properties", policy.Type, policy.Name)
}
switch policy.Type {
case v1alpha1.GarbageCollectPolicyType:
case v1alpha1.ApplyOncePolicyType:
@@ -390,6 +393,9 @@ func (p *Parser) parsePolicies(ctx context.Context, af *Appfile) (err error) {
return err
}
for _, policy := range af.Policies {
if policy.Properties == nil && policy.Type != v1alpha1.DebugPolicyType {
return fmt.Errorf("policy %s named %s must not have empty properties", policy.Type, policy.Name)
}
switch policy.Type {
case v1alpha1.GarbageCollectPolicyType:
case v1alpha1.ApplyOncePolicyType:

View File

@@ -243,6 +243,20 @@ spec:
image: "busybox"
`
const appfileYamlEmptyPolicy = `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: application-sample
namespace: default
spec:
components: []
policies:
- type: garbage-collect
name: somename
properties:
`
var _ = Describe("Test application parser", func() {
It("Test we can parse an application to an appFile", func() {
o := v1beta1.Application{}
@@ -282,6 +296,14 @@ var _ = Describe("Test application parser", func() {
Expect(err).ShouldNot(HaveOccurred())
_, err = NewApplicationParser(&tclient, dm, pd).GenerateAppFile(context.TODO(), &notfound)
Expect(err).Should(HaveOccurred())
By("app with empty policy")
emptyPolicy := v1beta1.Application{}
err = yaml.Unmarshal([]byte(appfileYamlEmptyPolicy), &emptyPolicy)
Expect(err).ShouldNot(HaveOccurred())
_, err = NewApplicationParser(&tclient, dm, pd).GenerateAppFile(context.TODO(), &emptyPolicy)
Expect(err).Should(HaveOccurred())
Expect(err.Error()).Should(ContainSubstring("have empty properties"))
})
})

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

@@ -86,4 +86,7 @@ type Args struct {
// IgnoreAppWithoutControllerRequirement indicates that application controller will not process the app without 'app.oam.dev/controller-version-require' annotation.
IgnoreAppWithoutControllerRequirement bool
// IgnoreDefinitionWithoutControllerRequirement indicates that trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation.
IgnoreDefinitionWithoutControllerRequirement bool
}

View File

@@ -136,7 +136,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
if annotations := app.GetAnnotations(); annotations == nil || annotations[oam.AnnotationKubeVelaVersion] == "" {
metav1.SetMetaDataAnnotation(&app.ObjectMeta, oam.AnnotationKubeVelaVersion, version.VelaVersion)
}
logCtx.AddTag("publish_version", app.GetAnnotations()[oam.AnnotationKubeVelaVersion])
logCtx.AddTag("publish_version", app.GetAnnotations()[oam.AnnotationPublishVersion])
appParser := appfile.NewApplicationParser(r.Client, r.dm, r.pd)
handler, err := NewAppHandler(logCtx, r, app, appParser)
@@ -307,6 +307,11 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
}))
defer subCtx.Commit("finish gc resourceTrackers")
statusUpdater := r.updateStatus
if isPatch {
statusUpdater = r.patchStatus
}
var options []resourcekeeper.GCOption
if !gcOutdated {
options = append(options, resourcekeeper.DisableMarkStageGCOption{}, resourcekeeper.DisableGCComponentRevisionOption{}, resourcekeeper.DisableLegacyGCOption{})
@@ -314,8 +319,10 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
finished, waiting, err := handler.resourceKeeper.GarbageCollect(logCtx, options...)
if err != nil {
logCtx.Error(err, "Failed to gc resourcetrackers")
r.Recorder.Event(handler.app, event.Warning(velatypes.ReasonFailedGC, err))
return r.endWithNegativeCondition(logCtx, handler.app, condition.ReconcileError(err), phase)
cond := condition.Deleting()
cond.Message = fmt.Sprintf("error encountered during garbage collection: %s", err.Error())
handler.app.Status.SetConditions(cond)
return r.result(statusUpdater(logCtx, handler.app, phase)).ret()
}
if !finished {
logCtx.Info("GarbageCollecting resourcetrackers unfinished")
@@ -324,13 +331,13 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
cond.Message = fmt.Sprintf("Waiting for %s to delete. (At least %d resources are deleting.)", waiting[0].DisplayName(), len(waiting))
}
handler.app.Status.SetConditions(cond)
return r.result(r.patchStatus(logCtx, handler.app, phase)).requeue(baseGCBackoffWaitTime).ret()
return r.result(statusUpdater(logCtx, handler.app, phase)).requeue(baseGCBackoffWaitTime).ret()
}
logCtx.Info("GarbageCollected resourcetrackers")
if !isPatch {
return r.result(r.updateStatus(logCtx, handler.app, common.ApplicationRunningWorkflow)).ret()
phase = common.ApplicationRunningWorkflow
}
return r.result(r.patchStatus(logCtx, handler.app, phase)).ret()
return r.result(statusUpdater(logCtx, handler.app, phase)).ret()
}
type reconcileResult struct {

View File

@@ -38,6 +38,7 @@ import (
"github.com/oam-dev/kubevela/pkg/monitor/metrics"
"github.com/oam-dev/kubevela/pkg/multicluster"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/resourcekeeper"
)
@@ -215,17 +216,14 @@ func (h *AppHandler) ProduceArtifacts(ctx context.Context, comps []*types.Compon
// nolint
func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Workload, appRev *v1beta1.ApplicationRevision, overrideNamespace string) (*common.ApplicationComponentStatus, bool, error) {
namespace := h.app.Namespace
if overrideNamespace != "" {
namespace = overrideNamespace
}
accessor := util.NewApplicationResourceNamespaceAccessor(h.app.Namespace, overrideNamespace)
var (
status = common.ApplicationComponentStatus{
Name: wl.Name,
WorkloadDefinition: wl.FullTemplate.Reference.Definition,
Healthy: true,
Namespace: namespace,
Namespace: accessor.Namespace(),
Cluster: multicluster.ClusterNameInContext(ctx),
}
appName = appRev.Spec.Application.Name
@@ -235,10 +233,10 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
if wl.CapabilityCategory == types.TerraformCategory {
var configuration terraforv1beta2.Configuration
if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: namespace}, &configuration); err != nil {
if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: accessor.Namespace()}, &configuration); err != nil {
if kerrors.IsNotFound(err) {
var legacyConfiguration terraforv1beta1.Configuration
if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: namespace}, &legacyConfiguration); err != nil {
if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: accessor.Namespace()}, &legacyConfiguration); err != nil {
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, check health error", appName, wl.Name)
}
isHealth = setStatus(&status, legacyConfiguration.Status.ObservedGeneration, legacyConfiguration.Generation,
@@ -251,12 +249,12 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
appRev.Name, configuration.Status.Apply.State, configuration.Status.Apply.Message)
}
} else {
if ok, err := wl.EvalHealth(wl.Ctx, h.r.Client, namespace); !ok || err != nil {
if ok, err := wl.EvalHealth(wl.Ctx, h.r.Client, accessor); !ok || err != nil {
isHealth = false
status.Healthy = false
}
status.Message, err = wl.EvalStatus(wl.Ctx, h.r.Client, namespace)
status.Message, err = wl.EvalStatus(wl.Ctx, h.r.Client, accessor)
if err != nil {
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, evaluate workload status message error", appName, wl.Name)
}
@@ -264,19 +262,26 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
var traitStatusList []common.ApplicationTraitStatus
for _, tr := range wl.Traits {
traitOverrideNamespace := overrideNamespace
if tr.FullTemplate.TraitDefinition.Spec.ControlPlaneOnly {
traitOverrideNamespace = appRev.GetNamespace()
wl.Ctx.SetCtx(context.WithValue(wl.Ctx.GetCtx(), multicluster.ClusterContextKey, multicluster.ClusterLocalName))
}
_accessor := util.NewApplicationResourceNamespaceAccessor(h.app.Namespace, traitOverrideNamespace)
var traitStatus = common.ApplicationTraitStatus{
Type: tr.Name,
Healthy: true,
}
if ok, err := tr.EvalHealth(wl.Ctx, h.r.Client, namespace); !ok || err != nil {
if ok, err := tr.EvalHealth(wl.Ctx, h.r.Client, _accessor); !ok || err != nil {
isHealth = false
traitStatus.Healthy = false
}
traitStatus.Message, err = tr.EvalStatus(wl.Ctx, h.r.Client, namespace)
traitStatus.Message, err = tr.EvalStatus(wl.Ctx, h.r.Client, _accessor)
if err != nil {
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)
wl.Ctx.SetCtx(context.WithValue(wl.Ctx.GetCtx(), multicluster.ClusterContextKey, status.Cluster))
}
status.Traits = traitStatusList
@@ -299,12 +304,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

@@ -41,6 +41,7 @@ import (
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/policy/envbinding"
"github.com/oam-dev/kubevela/pkg/utils"
"github.com/oam-dev/kubevela/pkg/velaql/providers/query"
"github.com/oam-dev/kubevela/pkg/workflow/providers"
"github.com/oam-dev/kubevela/pkg/workflow/providers/http"
"github.com/oam-dev/kubevela/pkg/workflow/providers/kube"
@@ -81,6 +82,7 @@ func (h *AppHandler) GenerateApplicationSteps(ctx monitorContext.Context,
terraformProvider.Install(handlerProviders, app, func(comp common.ApplicationComponent) (*appfile.Workload, error) {
return appParser.ParseWorkloadFromRevision(comp, appRev)
})
query.Install(handlerProviders, h.r.Client, nil)
var tasks []wfTypes.TaskRunner
for _, step := range af.WorkflowSteps {
@@ -188,7 +190,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

@@ -967,7 +967,7 @@ func (h historiesByComponentRevision) Less(i, j int) bool {
// UpdateApplicationRevisionStatus update application revision status
func (h *AppHandler) UpdateApplicationRevisionStatus(ctx context.Context, appRev *v1beta1.ApplicationRevision, succeed bool, wfStatus *common.WorkflowStatus) {
if appRev == nil {
if appRev == nil || DisableAllApplicationRevision {
return
}
appRev.Status.Succeeded = succeed

View File

@@ -43,17 +43,24 @@ import (
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/version"
)
// Reconciler reconciles a ComponentDefinition object
type Reconciler struct {
client.Client
dm discoverymapper.DiscoveryMapper
pd *packages.PackageDiscover
Scheme *runtime.Scheme
record event.Recorder
dm discoverymapper.DiscoveryMapper
pd *packages.PackageDiscover
Scheme *runtime.Scheme
record event.Recorder
options
}
type options struct {
defRevLimit int
concurrentReconciles int
ignoreDefNoCtrlReq bool
controllerVersion string
}
// Reconcile is the main logic for ComponentDefinition controller
@@ -68,6 +75,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if !r.matchControllerRequirement(&componentDefinition) {
klog.InfoS("skip componentDefinition: not match the controller requirement of componentDefinition", "componentDefinition", klog.KObj(&componentDefinition))
return ctrl.Result{}, nil
}
// refresh package discover when componentDefinition is registered
if componentDefinition.Spec.Workload.Type != types.AutoDetectWorkloadDefinition {
err := utils.RefreshPackageDiscover(ctx, r.Client, r.dm, r.pd, &componentDefinition)
@@ -187,12 +199,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
// Setup adds a controller that reconciles ComponentDefinition.
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
r := Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: args.DiscoveryMapper,
pd: args.PackageDiscover,
defRevLimit: args.DefRevisionLimit,
concurrentReconciles: args.ConcurrentReconciles,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: args.DiscoveryMapper,
pd: args.PackageDiscover,
options: parseOptions(args),
}
return r.SetupWithManager(mgr)
}
func parseOptions(args oamctrl.Args) options {
return options{
defRevLimit: args.DefRevisionLimit,
concurrentReconciles: args.ConcurrentReconciles,
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
controllerVersion: version.VelaVersion,
}
}
func (r *Reconciler) matchControllerRequirement(componentDefinition *v1beta1.ComponentDefinition) bool {
if componentDefinition.Annotations != nil {
if requireVersion, ok := componentDefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
return requireVersion == r.controllerVersion
}
}
if r.ignoreDefNoCtrlReq {
return false
}
return true
}

View File

@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
Expect(err).ToNot(HaveOccurred())
r = Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: dm,
pd: pd,
defRevLimit: defRevisionLimit,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: dm,
pd: pd,
options: options{
defRevLimit: defRevisionLimit,
},
}
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
var ctx context.Context

View File

@@ -44,6 +44,7 @@ import (
af "github.com/oam-dev/kubevela/pkg/appfile"
"github.com/oam-dev/kubevela/pkg/cue/process"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
const (
@@ -478,7 +479,8 @@ func CUEBasedHealthCheck(ctx context.Context, c client.Client, wlRef WorkloadRef
okToCheckTrait = true
return
}
isHealthy, err := wl.EvalHealth(pCtx, c, ns)
accessor := util.NewApplicationResourceNamespaceAccessor(ns, "")
isHealthy, err := wl.EvalHealth(pCtx, c, accessor)
if err != nil {
wlHealth.HealthStatus = StatusUnhealthy
wlHealth.Diagnosis = errors.Wrap(err, errHealthCheck).Error()
@@ -490,7 +492,7 @@ func CUEBasedHealthCheck(ctx context.Context, c client.Client, wlRef WorkloadRef
// TODO(wonderflow): we should add a custom way to let the template say why it's unhealthy, only a bool flag is not enough
wlHealth.HealthStatus = StatusUnhealthy
}
wlHealth.CustomStatusMsg, err = wl.EvalStatus(pCtx, c, ns)
wlHealth.CustomStatusMsg, err = wl.EvalStatus(pCtx, c, accessor)
if err != nil {
wlHealth.Diagnosis = errors.Wrap(err, errHealthCheck).Error()
}
@@ -522,7 +524,8 @@ func CUEBasedHealthCheck(ctx context.Context, c client.Client, wlRef WorkloadRef
traits[i] = tHealth
continue
}
isHealthy, err := tr.EvalHealth(pCtx, c, ns)
accessor := util.NewApplicationResourceNamespaceAccessor("", ns)
isHealthy, err := tr.EvalHealth(pCtx, c, accessor)
if err != nil {
tHealth.HealthStatus = StatusUnhealthy
tHealth.Diagnosis = errors.Wrap(err, errHealthCheck).Error()
@@ -535,7 +538,7 @@ func CUEBasedHealthCheck(ctx context.Context, c client.Client, wlRef WorkloadRef
// TODO(wonderflow): we should add a custom way to let the template say why it's unhealthy, only a bool flag is not enough
tHealth.HealthStatus = StatusUnhealthy
}
tHealth.CustomStatusMsg, err = tr.EvalStatus(pCtx, c, ns)
tHealth.CustomStatusMsg, err = tr.EvalStatus(pCtx, c, accessor)
if err != nil {
tHealth.Diagnosis = errors.Wrap(err, errHealthCheck).Error()
}

View File

@@ -42,17 +42,24 @@ import (
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/version"
)
// Reconciler reconciles a TraitDefinition object
type Reconciler struct {
client.Client
dm discoverymapper.DiscoveryMapper
pd *packages.PackageDiscover
Scheme *runtime.Scheme
record event.Recorder
dm discoverymapper.DiscoveryMapper
pd *packages.PackageDiscover
Scheme *runtime.Scheme
record event.Recorder
options
}
type options struct {
defRevLimit int
concurrentReconciles int
ignoreDefNoCtrlReq bool
controllerVersion string
}
// Reconcile is the main logic for TraitDefinition controller
@@ -67,6 +74,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if !r.matchControllerRequirement(&traitdefinition) {
klog.InfoS("skip traitDefinition: not match the controller requirement of traitDefinition", "traitDefinition", klog.KObj(&traitdefinition))
return ctrl.Result{}, nil
}
// this is a placeholder for finalizer here in the future
if traitdefinition.DeletionTimestamp != nil {
klog.InfoS("The TraitDefinition is being deleted", "traitDefinition", klog.KRef(req.Namespace, req.Name))
@@ -193,12 +205,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
// Setup adds a controller that reconciles TraitDefinition.
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
r := Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: args.DiscoveryMapper,
pd: args.PackageDiscover,
defRevLimit: args.DefRevisionLimit,
concurrentReconciles: args.ConcurrentReconciles,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: args.DiscoveryMapper,
pd: args.PackageDiscover,
options: parseOptions(args),
}
return r.SetupWithManager(mgr)
}
func parseOptions(args oamctrl.Args) options {
return options{
defRevLimit: args.DefRevisionLimit,
concurrentReconciles: args.ConcurrentReconciles,
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
controllerVersion: version.VelaVersion,
}
}
func (r *Reconciler) matchControllerRequirement(traitDefinition *v1beta1.TraitDefinition) bool {
if traitDefinition.Annotations != nil {
if requireVersion, ok := traitDefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
return requireVersion == r.controllerVersion
}
}
if r.ignoreDefNoCtrlReq {
return false
}
return true
}

View File

@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
Expect(err).ToNot(HaveOccurred())
r = Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: dm,
pd: pd,
defRevLimit: defRevisionLimit,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: dm,
pd: pd,
options: options{
defRevLimit: defRevisionLimit,
},
}
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
var ctx context.Context

View File

@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
Expect(err).ToNot(HaveOccurred())
r = Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: dm,
pd: pd,
defRevLimit: defRevisionLimit,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: dm,
pd: pd,
options: options{
defRevLimit: defRevisionLimit,
},
}
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
var ctx context.Context

View File

@@ -42,17 +42,24 @@ import (
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/version"
)
// Reconciler reconciles a WorkflowStepDefinition object
type Reconciler struct {
client.Client
dm discoverymapper.DiscoveryMapper
pd *packages.PackageDiscover
Scheme *runtime.Scheme
record event.Recorder
dm discoverymapper.DiscoveryMapper
pd *packages.PackageDiscover
Scheme *runtime.Scheme
record event.Recorder
options
}
type options struct {
defRevLimit int
concurrentReconciles int
ignoreDefNoCtrlReq bool
controllerVersion string
}
// Reconcile is the main logic for WorkflowStepDefinition controller
@@ -68,6 +75,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if !r.matchControllerRequirement(&wfstepdefinition) {
klog.InfoS("skip workflowStepDefinition: not match the controller requirement of workflowStepDefinition", "workflowStepDefinition", klog.KObj(&wfstepdefinition))
return ctrl.Result{}, nil
}
// this is a placeholder for finalizer here in the future
if wfstepdefinition.DeletionTimestamp != nil {
return ctrl.Result{}, nil
@@ -192,11 +204,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
// Setup adds a controller that reconciles WorkflowStepDefinition.
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
r := Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: args.DiscoveryMapper,
pd: args.PackageDiscover,
defRevLimit: args.DefRevisionLimit,
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
dm: args.DiscoveryMapper,
pd: args.PackageDiscover,
options: parseOptions(args),
}
return r.SetupWithManager(mgr)
}
func parseOptions(args oamctrl.Args) options {
return options{
defRevLimit: args.DefRevisionLimit,
concurrentReconciles: args.ConcurrentReconciles,
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
controllerVersion: version.VelaVersion,
}
}
func (r *Reconciler) matchControllerRequirement(wfstepdefinition *v1beta1.WorkflowStepDefinition) bool {
if wfstepdefinition.Annotations != nil {
if requireVersion, ok := wfstepdefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
return requireVersion == r.controllerVersion
}
}
if r.ignoreDefNoCtrlReq {
return false
}
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

@@ -62,8 +62,8 @@ const (
// AbstractEngine defines Definition's Render interface
type AbstractEngine interface {
Complete(ctx process.Context, abstractTemplate string, params interface{}) error
HealthCheck(ctx process.Context, cli client.Client, ns string, healthPolicyTemplate string) (bool, error)
Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string, parameter interface{}) (string, error)
HealthCheck(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, healthPolicyTemplate string) (bool, error)
Status(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, customStatusTemplate string, parameter interface{}) (string, error)
}
type def struct {
@@ -151,7 +151,7 @@ func (wd *workloadDef) Complete(ctx process.Context, abstractTemplate string, pa
return nil
}
func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader, ns string) (map[string]interface{}, error) {
func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader, accessor util.NamespaceAccessor) (map[string]interface{}, error) {
var root = initRoot(ctx.BaseContextLabels())
var commonLabels = GetCommonLabels(ctx.BaseContextLabels())
@@ -162,7 +162,7 @@ func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader
return nil, err
}
// workload main resource will have a unique label("app.oam.dev/resourceType"="WORKLOAD") in per component/app level
object, err := getResourceFromObj(ctx.GetCtx(), componentWorkload, cli, ns, util.MergeMapOverrideWithDst(map[string]string{
object, err := getResourceFromObj(ctx.GetCtx(), componentWorkload, cli, accessor.For(componentWorkload), util.MergeMapOverrideWithDst(map[string]string{
oam.LabelOAMResourceType: oam.ResourceTypeWorkload,
}, commonLabels), "")
if err != nil {
@@ -182,7 +182,7 @@ func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader
return nil, err
}
// AuxiliaryWorkload will have a unique label("trait.oam.dev/resource"="name of outputs") in per component/app level
object, err := getResourceFromObj(ctx.GetCtx(), traitRef, cli, ns, util.MergeMapOverrideWithDst(map[string]string{
object, err := getResourceFromObj(ctx.GetCtx(), traitRef, cli, accessor.For(componentWorkload), util.MergeMapOverrideWithDst(map[string]string{
oam.TraitTypeLabel: AuxiliaryWorkload,
}, commonLabels), assist.Name)
if err != nil {
@@ -197,11 +197,11 @@ func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader
}
// HealthCheck address health check for workload
func (wd *workloadDef) HealthCheck(ctx process.Context, cli client.Client, ns string, healthPolicyTemplate string) (bool, error) {
func (wd *workloadDef) HealthCheck(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, healthPolicyTemplate string) (bool, error) {
if healthPolicyTemplate == "" {
return true, nil
}
templateContext, err := wd.getTemplateContext(ctx, cli, ns)
templateContext, err := wd.getTemplateContext(ctx, cli, accessor)
if err != nil {
return false, errors.WithMessage(err, "get template context")
}
@@ -228,11 +228,11 @@ func checkHealth(templateContext map[string]interface{}, healthPolicyTemplate st
}
// Status get workload status by customStatusTemplate
func (wd *workloadDef) Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string, parameter interface{}) (string, error) {
func (wd *workloadDef) Status(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, customStatusTemplate string, parameter interface{}) (string, error) {
if customStatusTemplate == "" {
return "", nil
}
templateContext, err := wd.getTemplateContext(ctx, cli, ns)
templateContext, err := wd.getTemplateContext(ctx, cli, accessor)
if err != nil {
return "", errors.WithMessage(err, "get template context")
}
@@ -417,7 +417,7 @@ func initRoot(contextLabels map[string]string) map[string]interface{} {
return root
}
func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, ns string) (map[string]interface{}, error) {
func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, accessor util.NamespaceAccessor) (map[string]interface{}, error) {
var root = initRoot(ctx.BaseContextLabels())
var commonLabels = GetCommonLabels(ctx.BaseContextLabels())
@@ -431,7 +431,7 @@ func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, n
if err != nil {
return nil, err
}
object, err := getResourceFromObj(ctx.GetCtx(), traitRef, cli, ns, util.MergeMapOverrideWithDst(map[string]string{
object, err := getResourceFromObj(ctx.GetCtx(), traitRef, cli, accessor.For(traitRef), util.MergeMapOverrideWithDst(map[string]string{
oam.TraitTypeLabel: assist.Type,
}, commonLabels), assist.Name)
if err != nil {
@@ -446,11 +446,11 @@ func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, n
}
// Status get trait status by customStatusTemplate
func (td *traitDef) Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string, parameter interface{}) (string, error) {
func (td *traitDef) Status(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, customStatusTemplate string, parameter interface{}) (string, error) {
if customStatusTemplate == "" {
return "", nil
}
templateContext, err := td.getTemplateContext(ctx, cli, ns)
templateContext, err := td.getTemplateContext(ctx, cli, accessor)
if err != nil {
return "", errors.WithMessage(err, "get template context")
}
@@ -458,11 +458,11 @@ func (td *traitDef) Status(ctx process.Context, cli client.Client, ns string, cu
}
// HealthCheck address health check for trait
func (td *traitDef) HealthCheck(ctx process.Context, cli client.Client, ns string, healthPolicyTemplate string) (bool, error) {
func (td *traitDef) HealthCheck(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, healthPolicyTemplate string) (bool, error) {
if healthPolicyTemplate == "" {
return true, nil
}
templateContext, err := td.getTemplateContext(ctx, cli, ns)
templateContext, err := td.getTemplateContext(ctx, cli, accessor)
if err != nil {
return false, errors.WithMessage(err, "get template context")
}

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

@@ -718,10 +718,10 @@ func TestImports(t *testing.T) {
context: stepSessionID: "3w9qkdgn5w"`
v, err := NewValue(`
import (
"vela/op"
"vela/custom"
)
id: op.context.stepSessionID
id: custom.context.stepSessionID
`+cont, nil, cont)
assert.NilError(t, err)

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,17 +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),
}
if !clusterConfig.Cluster.InsecureSkipTLSVerify {
data["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
@@ -115,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 {
@@ -164,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()
@@ -172,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")
@@ -230,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 {
@@ -345,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
}
}
@@ -384,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 {
@@ -398,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")
}
@@ -416,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 (
@@ -198,7 +201,7 @@ const (
// AnnotationWorkloadName indicates the managed workload's name by trait
AnnotationWorkloadName = "trait.oam.dev/workload-name"
// AnnotationControllerRequirement indicates the controller version that can process the application.
// AnnotationControllerRequirement indicates the controller version that can process the application/definition.
AnnotationControllerRequirement = "app.oam.dev/controller-version-require"
// AnnotationApplicationServiceAccountName indicates the name of the ServiceAccount to use to apply Components and run Workflow.

View File

@@ -953,3 +953,38 @@ func AsController(r *corev1.ObjectReference) metav1.OwnerReference {
ref.Controller = &c
return ref
}
// NamespaceAccessor namespace accessor for resource
type NamespaceAccessor interface {
For(obj client.Object) string
Namespace() string
}
type applicationResourceNamespaceAccessor struct {
applicationNamespace string
overrideNamespace string
}
// For access namespace for resource
func (accessor *applicationResourceNamespaceAccessor) For(obj client.Object) string {
if accessor.overrideNamespace != "" {
return accessor.overrideNamespace
}
if originalNamespace := obj.GetNamespace(); originalNamespace != "" {
return originalNamespace
}
return accessor.applicationNamespace
}
// Namespace the namespace by default
func (accessor *applicationResourceNamespaceAccessor) Namespace() string {
if accessor.overrideNamespace != "" {
return accessor.overrideNamespace
}
return accessor.applicationNamespace
}
// NewApplicationResourceNamespaceAccessor create namespace accessor for resource in application
func NewApplicationResourceNamespaceAccessor(appNs, overrideNs string) NamespaceAccessor {
return &applicationResourceNamespaceAccessor{applicationNamespace: appNs, overrideNamespace: overrideNs}
}

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

@@ -81,3 +81,14 @@ func TestParseApplyOncePolicy(t *testing.T) {
r.NoError(err)
r.Equal(policySpec, spec)
}
func TestParsePolicy(t *testing.T) {
r := require.New(t)
// Test skipping empty policy
app := &v1beta1.Application{Spec: v1beta1.ApplicationSpec{
Policies: []v1beta1.AppPolicy{{Type: "example", Name: "s", Properties: nil}},
}}
exists, err := parsePolicy(app, "example", nil)
r.False(exists, "empty policy should not be included")
r.NoError(err)
}

View File

@@ -34,7 +34,7 @@ const (
// GetEnvBindingPolicy extract env-binding policy with given policy name, if policy name is empty, the first env-binding policy will be used
func GetEnvBindingPolicy(app *v1beta1.Application, policyName string) (*v1alpha1.EnvBindingSpec, error) {
for _, policy := range app.Spec.Policies {
if (policy.Name == policyName || policyName == "") && policy.Type == v1alpha1.EnvBindingPolicyType {
if (policy.Name == policyName || policyName == "") && policy.Type == v1alpha1.EnvBindingPolicyType && policy.Properties != nil {
envBindingSpec := &v1alpha1.EnvBindingSpec{}
err := json.Unmarshal(policy.Properties.Raw, envBindingSpec)
return envBindingSpec, err

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