Compare commits

..

25 Commits

Author SHA1 Message Date
github-actions[bot]
d748096f7c Fix: the endpoints is repeated and can not query the ingress with v1 version (#3864)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 31c28b6d00)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-05-11 14:34:28 +08:00
github-actions[bot]
d4ab93c232 Fix: ignore no kind match error in gc (#3863)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 0021f8823f)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-05-11 12:43:50 +08:00
Zheng Xi Zhou
37a656a292 Fix: update Terraform Configuration CRDS test file (#3857)
Updated Terraform Configuration CRDS to v1beta2 to fix the UT
issue of https://github.com/kubevela/kubevela/pull/3851

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-05-11 10:09:47 +08:00
github-actions[bot]
3c94ac1bc1 [Backport release-1.3] Fix(makefile): update kustomize version to be available for darwin-arm64 (#3858)
* Fix(makefile): update kustomize version to be available for darwin-arm64

Signed-off-by: Carmendelope <carmen@napptive.com>
(cherry picked from commit ad81fffe4f)

* make reviewable changes

Signed-off-by: Carmendelope <carmen@napptive.com>
(cherry picked from commit 84dd93425c)

Co-authored-by: Carmendelope <carmen@napptive.com>
2022-05-11 10:08:04 +08:00
StevenLeiZhang
8499dffcd7 Fix: The new addon can not shown in the Addons page (#3851)
Signed-off-by: StevenLeiZhang <zhangleiic@163.com>
2022-05-10 23:10:04 +08:00
StevenLeiZhang
30a6e85023 Fix: sensitive field of addon registry is exposed (#3852)
Signed-off-by: StevenLeiZhang <zhangleiic@163.com>
2022-05-10 20:25:10 +08:00
github-actions[bot]
11530e2720 Fix: add parse comments in lookupScript to make patch work (#3844)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 1758dc319d)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-05-10 13:38:30 +08:00
github-actions[bot]
f53466bc7d [Backport release-1.3] Fix: resolve locally installed addons not being displayed (#3842)
* Fix: resolve locally installed addons not being displayed

Addressed an issue where locally installed addons may not be displayed
if one with the same name is in the registry

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

* Style: revert incorrect auto-formatting

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

* Refactor: change original variable name to avoid confusions

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

* Test: add tests for outputs from `vela addon list`
when an addon with the same as registry one is locally installed

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

* Refactor: use more concise method to check length

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

* Test: add one more test condition for dual addons
i.e. local and registry

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

* Refactor: simplify testing logic by removing unneeded looping

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

* Style: add missing license header

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

Co-authored-by: Charlie Chiang <charlie_c_0129@outlook.com>
2022-05-10 13:37:37 +08:00
github-actions[bot]
e8ea8ec48f [Backport release-1.3] Fix: don't override user definied region (#3833)
* Fix: don't override user definied `region`

Fix #https://github.com/oam-dev/kubevela/issues/3384

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit 0b2f0e381f)

* fix check-diff

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit a9156212d0)

* fix CI

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit 423ce6ece1)

* fix CI

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit 5a827d2ef0)

* fix UT

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit 4b71568547)

* revert some changes

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit cde8d3a957)

Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-05-09 15:16:21 +08:00
github-actions[bot]
6c4c9bdf7e [Backport release-1.3] Fix: update latest version Fix: 1.2 upgrade 1.3 workflowstep XXX not found (#3819)
* Fix: 1.2 upgrade 1.3 workflowstep XXX not found

Signed-off-by: cezhang <c1zhang.dev@gmail.com>

handle publishversion case

Signed-off-by: cezhang <c1zhang.dev@gmail.com>
(cherry picked from commit 9cea9b0914)

* add test

Signed-off-by: cezhang <c1zhang.dev@gmail.com>

add test

Signed-off-by: cezhang <c1zhang.dev@gmail.com>

lint code

Signed-off-by: cezhang <c1zhang.dev@gmail.com>
(cherry picked from commit 10b2f691c1)

Co-authored-by: cezhang <c1zhang.dev@gmail.com>
2022-05-07 13:25:52 +08:00
github-actions[bot]
e7930a2da0 Fix: update latest version (#3796)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 202ccf7b68)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-04-29 17:50:43 +08:00
github-actions[bot]
45e1de19dc Fix: log message wraps wrong arguments (#3793)
Signed-off-by: StevenLeiZhang <zhangleiic@163.com>
(cherry picked from commit 79362f0648)

Co-authored-by: StevenLeiZhang <zhangleiic@163.com>
2022-04-29 13:34:15 +08:00
github-actions[bot]
d910bb7928 [Backport release-1.3] Chore: sync the cli binaries to OSS (#3785)
* Feat: show the parsing capability error message

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

* Chore: sync the cli binaries to OSS

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

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-04-29 10:14:43 +08:00
github-actions[bot]
a580c9a44c Fix: env trait compatible with valueFrom (#3784)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit d0506db414)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-04-28 18:19:01 +08:00
github-actions[bot]
8f5eaefd89 Fix: kubectl check err (#3779)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 3bbcbe0e6f)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-04-28 16:33:28 +08:00
github-actions[bot]
7c3a35ae87 [Backport release-1.3] Fix: addon cli parse any type (#3777)
* fix addon parse any type

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

* test int

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-28 16:08:48 +08:00
github-actions[bot]
ea0003f7cb Fix: fix revision in webservice (#3766)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 1ab13437b4)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-04-27 14:19:43 +08:00
Zheng Xi Zhou
3728857c82 Fix: use Terraform provider name as application in CLI (#3742) (#3756)
* Fix: use Terraform provider name as application in CLI

In CLI, use Terraform provider name as application name when
create a Provider. Also display there providers in VelaUX.
1). manually created a Terraform Provider object, like https://github.com/oam-dev/terraform-controller/blob/master/getting-started.md#aws
2). by enabling a Terraform provider addon in version older than v1.3.0
3). by create a Terraform provider via `vela provider add`
4). by VelaUX

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-04-26 22:17:17 +08:00
github-actions[bot]
8e6c49cb37 [Backport release-1.3] Fix: fix the bug of vela cli enable addon by localDir on windows os (#3762)
* fix windows bug

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

fix several issue

fix bug

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

fix unit-test

(cherry picked from commit 956dff3261)

* add more tests

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-26 20:53:34 +08:00
github-actions[bot]
4b31274bda [Backport release-1.3] Fix: velaux addon hint after enable (#3760)
* Fix: velaux addon hint after enable

Signed-off-by: qiaozp <chivalry.pp@gmail.com>
(cherry picked from commit 5adfd6210f)

* check if upgrade

Signed-off-by: qiaozp <chivalry.pp@gmail.com>
(cherry picked from commit 5a7467a494)

Co-authored-by: qiaozp <chivalry.pp@gmail.com>
2022-04-26 16:47:36 +08:00
github-actions[bot]
429e62d11b [Backport release-1.3] Feat: check whether a project matched a config's project (#3757)
* Feat: check whether a project matched a config's project

If the config project is not nil, it's matched whether the project
matched the target project.
If the config project is nil, the target project matched the config.

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit dca9646693)

Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-04-26 14:59:41 +08:00
github-actions[bot]
841a18189a Fix: public image registry config could not be created (#3748)
Fix #3663

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit 4fc599b8c9)

Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-04-25 13:59:00 +08:00
github-actions[bot]
59bd066c05 use unical project filter func to list secret (#3746)
Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix pointer

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-25 10:06:37 +08:00
github-actions[bot]
efa9dedb85 Fix: vela-cli does not print cluster name, if application installed in default cluster (#3740)
Signed-off-by: StevenLeiZhang <zhangleiic@163.com>
(cherry picked from commit 3819981dd4)

Co-authored-by: StevenLeiZhang <zhangleiic@163.com>
2022-04-24 09:17:39 +08:00
Xiangbo Ma
fdffde4dfd Fix: cherry-pick #3724 to delete apprev annotation. Signed-off-by: Xiangbo Ma <maxiangboo@cmbchina.com> (#3739)
Signed-off-by: fourierr <maxiangboo@qq.com>
2022-04-24 09:17:15 +08:00
75 changed files with 4003 additions and 766 deletions

View File

@@ -8,6 +8,10 @@ on:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUCKET: ${{ secrets.CLI_OSS_BUCKET }}
ENDPOINT: ${{ secrets.CLI_OSS_ENDPOINT }}
ACCESS_KEY: ${{ secrets.CLI_OSS_ACCESS_KEY }}
ACCESS_KEY_SECRET: ${{ secrets.CLI_OSS_ACCESS_KEY_SECRET }}
jobs:
build:
@@ -104,6 +108,23 @@ jobs:
name: sha256sums
path: ./_bin/sha256-${{ steps.get_matrix.outputs.OS }}-${{ steps.get_matrix.outputs.ARCH }}.txt
retention-days: 1
- name: clear the asset
run: |
rm -rf ./_bin/vela/${{ steps.get_matrix.outputs.OS }}-${{ steps.get_matrix.outputs.ARCH }}
mv ./_bin/vela/vela-${{ steps.get_matrix.outputs.OS }}-${{ steps.get_matrix.outputs.ARCH }}.tar.gz ./_bin/vela/vela-${{ env.VELA_VERSION }}-${{ steps.get_matrix.outputs.OS }}-${{ steps.get_matrix.outputs.ARCH }}.tar.gz
mv ./_bin/vela/vela-${{ steps.get_matrix.outputs.OS }}-${{ steps.get_matrix.outputs.ARCH }}.zip ./_bin/vela/vela-${{ env.VELA_VERSION }}-${{ steps.get_matrix.outputs.OS }}-${{ steps.get_matrix.outputs.ARCH }}.zip
- 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 local to cloud
run: ./ossutil --config-file .ossutilconfig sync ./_bin/vela oss://$BUCKET/binary/vela/${{ env.VELA_VERSION }}
- name: sync the latest version file
run: |
echo ${{ env.VELA_VERSION }} > ./latest_version
./ossutil --config-file .ossutilconfig cp -u ./latest_version oss://$BUCKET/binary/vela/latest_version
upload-plugin-homebrew:
needs: build

View File

@@ -150,6 +150,13 @@ const (
)
const (
// TerrfaormComponentPrefix is the prefix of component type of terraform-xxx
TerrfaormComponentPrefix = "terraform-"
// TerraformComponentPrefix is the prefix of component type of terraform-xxx
TerraformComponentPrefix = "terraform-"
// ProviderAppPrefix is the prefix of the application to create a Terraform Provider
ProviderAppPrefix = "config-terraform-provider"
// ProviderNamespace is the namespace of Terraform Cloud Provider
ProviderNamespace = "default"
// VelaCoreConfig is to mark application, config and its secret or Terraform provider lelong to a KubeVela config
VelaCoreConfig = "velacore-config"
)

File diff suppressed because it is too large Load Diff

View File

@@ -36,20 +36,23 @@ spec:
"config.oam.dev/sub-type": "auth"
}
}
type: "kubernetes.io/dockerconfigjson"
stringData: {
if parameter.auth != _|_ {
".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))
if parameter.auth != _|_ {
type: "kubernetes.io/dockerconfigjson"
}
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
}
})
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
})
}
}
parameter: {

View File

@@ -46,7 +46,7 @@ spec:
}]
}
if _baseEnv != _|_ {
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar.value}}
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar}}
// +patchStrategy=replace
env: [ for envVar in _baseEnv if _delKeys[envVar.name] == _|_ && !_params.replace {
name: envVar.name
@@ -54,11 +54,15 @@ spec:
value: _params.env[envVar.name]
}
if _params.env[envVar.name] == _|_ {
value: envVar.value
if envVar.value != _|_ {
value: envVar.value
}
if envVar.valueFrom != _|_ {
valueFrom: envVar.valueFrom
}
}
}] + [ for k, v in _params.env if _delKeys[k] == _|_ && (_params.replace || _baseEnvMap[k] == _|_) {
name: k
value: v
v
}]
}
}

View File

@@ -132,10 +132,9 @@ spec:
parameter.labels
}
if parameter.addRevisionLabel {
"app.oam.dev/appRevision": context.appRevision
"app.oam.dev/revision": context.revision
}
"app.oam.dev/component": context.name
"app.oam.dev/revision": context.revision
}
if parameter.annotations != _|_ {
annotations: parameter.annotations
@@ -333,7 +332,7 @@ spec:
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
// +ignore
// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
// +usage=If addRevisionLabel is true, the revision label will be added to the underlying pods
addRevisionLabel: *false | bool
// +usage=Commands to run in the container

File diff suppressed because it is too large Load Diff

View File

@@ -36,20 +36,23 @@ spec:
"config.oam.dev/sub-type": "auth"
}
}
type: "kubernetes.io/dockerconfigjson"
stringData: {
if parameter.auth != _|_ {
".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))
if parameter.auth != _|_ {
type: "kubernetes.io/dockerconfigjson"
}
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
}
})
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
})
}
}
parameter: {

View File

@@ -46,7 +46,7 @@ spec:
}]
}
if _baseEnv != _|_ {
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar.value}}
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar}}
// +patchStrategy=replace
env: [ for envVar in _baseEnv if _delKeys[envVar.name] == _|_ && !_params.replace {
name: envVar.name
@@ -54,11 +54,15 @@ spec:
value: _params.env[envVar.name]
}
if _params.env[envVar.name] == _|_ {
value: envVar.value
if envVar.value != _|_ {
value: envVar.value
}
if envVar.valueFrom != _|_ {
valueFrom: envVar.valueFrom
}
}
}] + [ for k, v in _params.env if _delKeys[k] == _|_ && (_params.replace || _baseEnvMap[k] == _|_) {
name: k
value: v
v
}]
}
}

View File

@@ -132,10 +132,9 @@ spec:
parameter.labels
}
if parameter.addRevisionLabel {
"app.oam.dev/appRevision": context.appRevision
"app.oam.dev/revision": context.revision
}
"app.oam.dev/component": context.name
"app.oam.dev/revision": context.revision
}
if parameter.annotations != _|_ {
annotations: parameter.annotations
@@ -333,7 +332,7 @@ spec:
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
// +ignore
// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
// +usage=If addRevisionLabel is true, the revision label will be added to the underlying pods
addRevisionLabel: *false | bool
// +usage=Commands to run in the container

12
go.mod
View File

@@ -63,11 +63,11 @@ require (
github.com/wonderflow/cert-manager-api v1.0.3
go.mongodb.org/mongo-driver v1.5.1
go.uber.org/zap v1.18.1
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/tools v0.1.6 // indirect
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/src-d/go-git.v4 v4.13.1
@@ -108,7 +108,7 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/BurntSushi/toml v0.4.1 // indirect
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
@@ -251,8 +251,8 @@ require (
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect

25
go.sum
View File

@@ -110,8 +110,9 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
@@ -1656,7 +1657,7 @@ github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
@@ -1798,8 +1799,9 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1843,8 +1845,9 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1909,9 +1912,10 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1924,8 +1928,9 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -2048,7 +2053,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
@@ -2204,8 +2209,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6 h1:SIasE1FVIQOWz2GEAHFOmoW7xchJcqlucjSULTL0Ag4=
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a h1:ofrrl6c6NG5/IOSx/R1cyiQxxjqlur0h/TvbUhkH0II=
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -56,7 +56,7 @@ else
CUE=$(shell which cue)
endif
KUSTOMIZE_VERSION ?= 3.8.2
KUSTOMIZE_VERSION ?= 4.5.4
.PHONY: kustomize
kustomize:

View File

@@ -195,6 +195,10 @@ func GetPatternFromItem(it Item, r AsyncReader, rootPath string) string {
if strings.HasPrefix(relativePath, strings.Join([]string{rootPath, p.Value}, "/")) {
return p.Value
}
if strings.HasPrefix(relativePath, filepath.Join(rootPath, p.Value)) {
// for enable addon by load dir, compatible with linux or windows os
return p.Value
}
}
return ""
}
@@ -357,7 +361,7 @@ func readResFile(a *InstallPackage, reader AsyncReader, readPath string) error {
if filename == "parameter.cue" {
return nil
}
file := ElementFile{Data: b, Name: path.Base(readPath)}
file := ElementFile{Data: b, Name: filepath.Base(readPath)}
switch filepath.Ext(filename) {
case ".cue":
a.CUETemplates = append(a.CUETemplates, file)
@@ -375,7 +379,7 @@ func readDefSchemaFile(a *InstallPackage, reader AsyncReader, readPath string) e
if err != nil {
return err
}
a.DefSchemas = append(a.DefSchemas, ElementFile{Data: b, Name: path.Base(readPath)})
a.DefSchemas = append(a.DefSchemas, ElementFile{Data: b, Name: filepath.Base(readPath)})
return nil
}
@@ -386,7 +390,7 @@ func readDefFile(a *UIData, reader AsyncReader, readPath string) error {
return err
}
filename := path.Base(readPath)
file := ElementFile{Data: b, Name: path.Base(readPath)}
file := ElementFile{Data: b, Name: filepath.Base(readPath)}
switch filepath.Ext(filename) {
case ".cue":
a.CUEDefinitions = append(a.CUEDefinitions, file)

View File

@@ -21,6 +21,8 @@ import (
"fmt"
"time"
"github.com/oam-dev/kubevela/pkg/utils/apply"
"github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -362,6 +364,21 @@ var _ = Describe("func addon update ", func() {
})
})
var _ = Describe("test enable addon in local dir", func() {
BeforeEach(func() {
app := v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-example"}}
Expect(k8sClient.Delete(ctx, &app)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
})
It("test enable addon by local dir", func() {
ctx := context.Background()
err := EnableAddonByLocalDir(ctx, "example", "./testdata/example", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, map[string]interface{}{"example": "test"})
Expect(err).Should(BeNil())
app := v1beta1.Application{}
Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-example"}, &app)).Should(BeNil())
})
})
const (
appYaml = `apiVersion: core.oam.dev/v1beta1
kind: Application

View File

@@ -19,12 +19,12 @@ package addon
import (
"context"
"fmt"
"strings"
"sync"
"time"
"github.com/oam-dev/kubevela/pkg/utils"
"github.com/oam-dev/kubevela/pkg/apiserver/log"
"github.com/oam-dev/kubevela/pkg/utils"
)
// We have three addon layer here
@@ -119,28 +119,27 @@ func (u *Cache) GetUIData(r Registry, addonName, version string) (*UIData, error
// ListUIData will always list UIData from cache first, if not exist, read from source.
func (u *Cache) ListUIData(r Registry) ([]*UIData, error) {
var err error
listAddons := u.listCachedUIData(r.Name)
if listAddons != nil {
return listAddons, nil
}
var listAddons []*UIData
if !IsVersionRegistry(r) {
addonMeta, err := u.ListAddonMeta(r)
listAddons = u.listCachedUIData(r.Name)
if listAddons != nil {
return listAddons, nil
}
listAddons, err = u.listUIDataAndCache(r)
if err != nil {
return nil, err
}
listAddons, err = r.ListUIData(addonMeta, UIMetaOptions)
if err != nil {
return nil, fmt.Errorf("fail to get addons from registry %s, %w", r.Name, err)
}
} else {
versionedRegistry := BuildVersionedRegistry(r.Name, r.Helm.URL)
listAddons, err = versionedRegistry.ListAddon()
listAddons = u.listVersionRegistryCachedUIData(r.Name)
if listAddons != nil {
return listAddons, nil
}
listAddons, err = u.listVersionRegistryUIDataAndCache(r)
if err != nil {
log.Logger.Errorf("fail to get addons from registry %s for cache updating, %v", r.Name, err)
return nil, err
}
}
u.putAddonUIData2Cache(r.Name, listAddons)
return listAddons, nil
}
@@ -175,6 +174,27 @@ func (u *Cache) listCachedUIData(name string) []*UIData {
return d
}
// listVersionRegistryCachedUIData will get cached addons from specified VersionRegistry in cache
func (u *Cache) listVersionRegistryCachedUIData(name string) []*UIData {
if u == nil {
return nil
}
u.mutex.RLock()
defer u.mutex.RUnlock()
d, ok := u.versionedUIData[name]
if !ok {
return nil
}
var uiDatas []*UIData
for version, uiData := range d {
if !strings.Contains(version, "-latest") {
uiDatas = append(uiDatas, uiData)
}
}
return uiDatas
}
// getCachedAddonMeta will get cached registry meta from specified registry in cache
func (u *Cache) getCachedAddonMeta(name string) map[string]SourceMeta {
if u == nil {
@@ -260,35 +280,51 @@ func (u *Cache) discoverAndRefreshRegistry() {
for _, r := range registries {
if !IsVersionRegistry(r) {
registryMeta, err := r.ListAddonMeta()
_, err = u.listUIDataAndCache(r)
if err != nil {
log.Logger.Errorf("fail to list registry %s metadata, %v", r.Name, err)
continue
}
u.putAddonMeta2Cache(r.Name, registryMeta)
uiData, err := r.ListUIData(registryMeta, UIMetaOptions)
if err != nil {
log.Logger.Errorf("fail to get addons from registry %s for cache updating, %v", r.Name, err)
continue
}
u.putAddonUIData2Cache(r.Name, uiData)
} else {
versionedRegistry := BuildVersionedRegistry(r.Name, r.Helm.URL)
uiDatas, err := versionedRegistry.ListAddon()
_, err = u.listVersionRegistryUIDataAndCache(r)
if err != nil {
log.Logger.Errorf("fail to get addons from registry %s for cache updating, %v", r.Name, err)
continue
}
for _, addon := range uiDatas {
uiData, err := versionedRegistry.GetAddonUIData(context.Background(), addon.Name, addon.Version)
if err != nil {
log.Logger.Errorf("fail to get addon from registry %s, addon %s version %s for cache updating, %v", addon.Name, r.Name, err)
continue
}
u.putVersionedUIData2Cache(r.Name, addon.Name, addon.Version, uiData)
// we also no version key, if use get addonUIData without version will return this vale as latest data.
u.putVersionedUIData2Cache(r.Name, addon.Name, "latest", uiData)
}
}
}
}
func (u *Cache) listUIDataAndCache(r Registry) ([]*UIData, error) {
registryMeta, err := r.ListAddonMeta()
if err != nil {
log.Logger.Errorf("fail to list registry %s metadata, %v", r.Name, err)
return nil, err
}
u.putAddonMeta2Cache(r.Name, registryMeta)
uiData, err := r.ListUIData(registryMeta, UIMetaOptions)
if err != nil {
log.Logger.Errorf("fail to get addons from registry %s for cache updating, %v", r.Name, err)
return nil, err
}
u.putAddonUIData2Cache(r.Name, uiData)
return uiData, nil
}
func (u *Cache) listVersionRegistryUIDataAndCache(r Registry) ([]*UIData, error) {
versionedRegistry := BuildVersionedRegistry(r.Name, r.Helm.URL)
uiDatas, err := versionedRegistry.ListAddon()
if err != nil {
log.Logger.Errorf("fail to get addons from registry %s for cache updating, %v", r.Name, err)
return nil, err
}
for _, addon := range uiDatas {
uiData, err := versionedRegistry.GetAddonUIData(context.Background(), addon.Name, addon.Version)
if err != nil {
log.Logger.Errorf("fail to get addon from versioned registry %s, addon %s version %s for cache updating, %v", r.Name, addon.Name, addon.Version, err)
continue
}
u.putVersionedUIData2Cache(r.Name, addon.Name, addon.Version, uiData)
// we also no version key, if use get addonUIData without version will return this vale as latest data.
u.putVersionedUIData2Cache(r.Name, addon.Name, "latest", uiData)
}
return uiDatas, nil
}

View File

@@ -31,3 +31,63 @@ func TestPutVersionedUIData2cache(t *testing.T) {
assert.NotEmpty(t, u.versionedUIData["helm-repo"]["fluxcd-1.0.0"])
assert.Equal(t, u.versionedUIData["helm-repo"]["fluxcd-1.0.0"].Name, "fluxcd")
}
func TestPutAddonUIData2Cache(t *testing.T) {
uiData := UIData{Meta: Meta{Name: "fluxcd", Icon: "test.com/fluxcd.png", Version: "1.0.0"}}
addons := []*UIData{&uiData}
name := "helm-repo"
u := NewCache(nil)
u.putAddonUIData2Cache(name, addons)
assert.NotEmpty(t, u.uiData)
assert.Equal(t, u.uiData[name], addons)
}
func TestListCachedUIData(t *testing.T) {
uiData := UIData{Meta: Meta{Name: "fluxcd", Icon: "test.com/fluxcd.png", Version: "1.0.0"}}
addons := []*UIData{&uiData}
name := "helm-repo"
u := NewCache(nil)
u.putAddonUIData2Cache(name, addons)
assert.Equal(t, u.listCachedUIData(name), addons)
}
func TestPutAddonMeta2Cache(t *testing.T) {
addonMeta := map[string]SourceMeta{
"fluxcd": {
Name: "fluxcd",
Items: []Item{
&OSSItem{
tp: FileType,
path: "fluxcd/definitions/helm-release.yaml",
name: "helm-release.yaml",
},
},
},
}
name := "helm-repo"
u := NewCache(nil)
u.putAddonMeta2Cache(name, addonMeta)
assert.NotEmpty(t, u.registryMeta)
assert.Equal(t, u.registryMeta[name], addonMeta)
}
func TestGetCachedAddonMeta(t *testing.T) {
addonMeta := map[string]SourceMeta{
"fluxcd": {
Name: "fluxcd",
Items: []Item{
&OSSItem{
tp: FileType,
path: "fluxcd/definitions/helm-release.yaml",
name: "helm-release.yaml",
},
},
},
}
name := "helm-repo"
u := NewCache(nil)
u.putAddonMeta2Cache(name, addonMeta)
assert.Equal(t, u.getCachedAddonMeta(name), addonMeta)
}

View File

@@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"path/filepath"
"k8s.io/client-go/discovery"
"k8s.io/klog/v2"
@@ -92,7 +93,11 @@ 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 {
r := localReader{dir: dir, name: name}
absDir, err := filepath.Abs(dir)
if err != nil {
return err
}
r := localReader{dir: absDir, name: name}
metas, err := r.ListAddonMeta()
if err != nil {
return err

View File

@@ -37,8 +37,10 @@ func (l localReader) ListAddonMeta() (map[string]SourceMeta, error) {
}
func (l localReader) ReadFile(path string) (string, error) {
file := strings.TrimPrefix(path, l.name+"/")
b, err := ioutil.ReadFile(filepath.Clean(filepath.Join(l.dir, file)))
path = strings.TrimPrefix(path, l.name+"/")
// for windows
path = strings.TrimPrefix(path, l.name+"\\")
b, err := ioutil.ReadFile(filepath.Clean(filepath.Join(l.dir, path)))
if err != nil {
return "", err
}
@@ -46,7 +48,8 @@ func (l localReader) ReadFile(path string) (string, error) {
}
func (l localReader) RelativePath(item Item) string {
return filepath.Join(l.name, strings.TrimPrefix(item.GetPath()+"/", l.dir))
file := strings.TrimPrefix(item.GetPath(), filepath.Clean(l.dir))
return filepath.Join(l.name, file)
}
func recursiveFetchFiles(path string, metas *SourceMeta) error {
@@ -60,7 +63,7 @@ func recursiveFetchFiles(path string, metas *SourceMeta) error {
return err
}
} else {
metas.Items = append(metas.Items, OSSItem{tp: "file", path: fmt.Sprintf("%s/%s", path, file.Name()), name: file.Name()})
metas.Items = append(metas.Items, OSSItem{tp: "file", path: filepath.Join(path, file.Name()), name: file.Name()})
}
}
return nil

View File

@@ -68,6 +68,43 @@ type HelmSource struct {
URL string `json:"url,omitempty" validate:"required"`
}
// SafeCopier is an interface to copy Struct without sensitive fields, such as Token, Username, Password
type SafeCopier interface {
SafeCopy() interface{}
}
// SafeCopy hides field Token
func (g *GitAddonSource) SafeCopy() *GitAddonSource {
if g == nil {
return nil
}
return &GitAddonSource{
URL: g.URL,
Path: g.Path,
}
}
// SafeCopy hides field Token
func (g *GiteeAddonSource) SafeCopy() *GiteeAddonSource {
if g == nil {
return nil
}
return &GiteeAddonSource{
URL: g.URL,
Path: g.Path,
}
}
// SafeCopy hides field Username, Password
func (h *HelmSource) SafeCopy() *HelmSource {
if h == nil {
return nil
}
return &HelmSource{
URL: h.URL,
}
}
// Item is a partial interface for github.RepositoryContent
type Item interface {
// GetType return "dir" or "file"

View File

@@ -115,3 +115,30 @@ func TestConvert2OssItem(t *testing.T) {
assert.Equal(t, expectItemCase, addonMetas)
}
func TestSafeCopy(t *testing.T) {
var git *GitAddonSource
sgit := git.SafeCopy()
assert.Nil(t, sgit)
git = &GitAddonSource{URL: "http://github.com/kubevela", Path: "addons", Token: "123456"}
sgit = git.SafeCopy()
assert.Empty(t, sgit.Token)
assert.Equal(t, "http://github.com/kubevela", sgit.URL)
assert.Equal(t, "addons", sgit.Path)
var gitee *GiteeAddonSource
sgitee := gitee.SafeCopy()
assert.Nil(t, sgitee)
gitee = &GiteeAddonSource{URL: "http://gitee.com/kubevela", Path: "addons", Token: "123456"}
sgitee = gitee.SafeCopy()
assert.Empty(t, sgitee.Token)
assert.Equal(t, "http://gitee.com/kubevela", sgitee.URL)
assert.Equal(t, "addons", sgitee.Path)
var helm *HelmSource
shelm := helm.SafeCopy()
assert.Nil(t, shelm)
helm = &HelmSource{URL: "https://hub.vela.com/chartrepo/addons"}
shelm = helm.SafeCopy()
assert.Equal(t, "https://hub.vela.com/chartrepo/addons", shelm.URL)
}

View File

@@ -314,10 +314,10 @@ func (u *defaultAddonHandler) CreateAddonRegistry(ctx context.Context, req apis.
func convertAddonRegistry(r pkgaddon.Registry) *apis.AddonRegistry {
return &apis.AddonRegistry{
Name: r.Name,
Git: r.Git,
Gitee: r.Gitee,
Git: r.Git.SafeCopy(),
Gitee: r.Gitee.SafeCopy(),
OSS: r.OSS,
Helm: r.Helm,
Helm: r.Helm.SafeCopy(),
}
}

View File

@@ -23,6 +23,7 @@ import (
"strings"
set "github.com/deckarep/golang-set"
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
@@ -38,13 +39,13 @@ import (
"github.com/oam-dev/kubevela/pkg/apiserver/model"
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
"github.com/oam-dev/kubevela/pkg/definition"
"github.com/oam-dev/kubevela/pkg/utils/config"
)
const (
definitionAlias = definition.UserPrefix + "alias.config.oam.dev"
definitionType = definition.UserPrefix + "type.config.oam.dev"
velaCoreConfig = "velacore-config"
configIsReady = "Ready"
configIsNotReady = "Not ready"
terraformProviderAlias = "Terraform Cloud Provider"
@@ -82,7 +83,7 @@ type configUseCaseImpl struct {
func (u *configUseCaseImpl) ListConfigTypes(ctx context.Context, query string) ([]*apis.ConfigType, error) {
defs := &v1beta1.ComponentDefinitionList{}
if err := u.kubeClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig}); err != nil {
client.MatchingLabels{definition.UserPrefix + "catalog.config.oam.dev": types.VelaCoreConfig}); err != nil {
return nil, err
}
@@ -137,7 +138,7 @@ func (u *configUseCaseImpl) GetConfigType(ctx context.Context, configType string
func (u *configUseCaseImpl) CreateConfig(ctx context.Context, req apis.CreateConfigRequest) error {
p := req.Properties
// If the component is Terraform type, set the provider name same as the application name and the component name
if strings.HasPrefix(req.ComponentType, types.TerrfaormComponentPrefix) {
if strings.HasPrefix(req.ComponentType, types.TerraformComponentPrefix) {
var properties map[string]interface{}
if err := json.Unmarshal([]byte(p), &properties); err != nil {
return errors.Wrapf(err, "unable to process the properties of %s", req.ComponentType)
@@ -149,52 +150,45 @@ func (u *configUseCaseImpl) CreateConfig(ctx context.Context, req apis.CreateCon
}
p = string(tmp)
}
app := v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: req.Name,
Namespace: types.DefaultKubeVelaNS,
Annotations: map[string]string{
types.AnnotationConfigAlias: req.Alias,
types.AnnotationConfigDescription: req.Description,
},
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigType: req.ComponentType,
types.LabelConfigProject: req.Project,
},
},
Spec: v1beta1.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: req.Name,
Type: req.ComponentType,
Properties: &runtime.RawExtension{Raw: []byte(p)},
},
},
},
ui := config.UIParam{
Alias: req.Alias,
Description: req.Description,
Project: req.Project,
}
return u.kubeClient.Create(ctx, &app)
return config.CreateApplication(ctx, u.kubeClient, req.Name, req.ComponentType, p, ui)
}
func (u *configUseCaseImpl) GetConfigs(ctx context.Context, configType string) ([]*apis.Config, error) {
switch configType {
case types.TerraformProvider:
defs := &v1beta1.ComponentDefinitionList{}
if err := u.kubeClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definition.UserPrefix + "type.config.oam.dev": types.TerraformProvider,
}); err != nil {
providers, err := config.ListTerraformProviders(ctx, u.kubeClient)
if err != nil {
return nil, err
}
var configs []*apis.Config
for _, d := range defs.Items {
subConfigs, err := u.getConfigsByConfigType(ctx, d.Name)
if err != nil {
configs := make([]*apis.Config, len(providers))
for i, p := range providers {
var a v1beta1.Application
if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: p.Name}, &a); err != nil {
if kerrors.IsNotFound(err) {
t := p.CreationTimestamp.Time
configs[i] = &apis.Config{
Name: p.Name,
CreatedTime: &t,
}
if p.Status.State == terraformtypes.ProviderIsReady {
configs[i].Status = configIsReady
} else {
configs[i].Status = configIsNotReady
}
continue
}
return nil, err
}
configs = append(configs, subConfigs...)
// If the application doesn't have any components, skip it as something wrong happened.
if !strings.HasPrefix(a.Labels[types.LabelConfigType], types.TerraformComponentPrefix) {
continue
}
configs[i] = retrieveConfigFromApplication(a, a.Labels[types.LabelConfigProject])
}
return configs, nil
@@ -209,7 +203,7 @@ func (u *configUseCaseImpl) getConfigsByConfigType(ctx context.Context, configTy
if err := u.kubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigType: configType,
}); err != nil {
return nil, err
@@ -218,12 +212,6 @@ func (u *configUseCaseImpl) getConfigsByConfigType(ctx context.Context, configTy
configs := make([]*apis.Config, len(apps.Items))
for i, a := range apps.Items {
configs[i] = retrieveConfigFromApplication(a, a.Labels[types.LabelConfigProject])
switch a.Status.Phase {
case common.ApplicationRunning:
configs[i].Status = configIsReady
default:
configs[i].Status = configIsNotReady
}
}
return configs, nil
}
@@ -245,11 +233,11 @@ func (u *configUseCaseImpl) GetConfig(ctx context.Context, configType, name stri
}
func (u *configUseCaseImpl) DeleteConfig(ctx context.Context, configType, name string) error {
var a = &v1beta1.Application{}
if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, a); err != nil {
return err
var isTerraformProvider bool
if strings.HasPrefix(configType, types.TerraformComponentPrefix) {
isTerraformProvider = true
}
return u.kubeClient.Delete(ctx, a)
return config.DeleteApplication(ctx, u.kubeClient, name, isTerraformProvider)
}
// ApplicationDeployTarget is the struct of application deploy target
@@ -265,7 +253,7 @@ func SyncConfigs(ctx context.Context, k8sClient client.Client, project string, t
var secrets v1.SecretList
if err := k8sClient.List(ctx, &secrets, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigSyncToMultiCluster: "true",
}); err != nil {
return err
@@ -324,7 +312,7 @@ func SyncConfigs(ctx context.Context, k8sClient client.Client, project string, t
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigProject: project,
},
},

View File

@@ -22,8 +22,11 @@ import (
"sort"
"strings"
"testing"
"time"
. "github.com/agiledragon/gomonkey/v2"
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
"gotest.tools/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -32,6 +35,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"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"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
@@ -53,7 +57,7 @@ func TestListConfigTypes(t *testing.T) {
Name: "def1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definition.UserPrefix + "catalog.config.oam.dev": types.VelaCoreConfig,
definitionType: types.TerraformProvider,
},
},
@@ -70,7 +74,7 @@ func TestListConfigTypes(t *testing.T) {
definitionAlias: "Def2",
},
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definition.UserPrefix + "catalog.config.oam.dev": types.VelaCoreConfig,
},
},
}
@@ -150,7 +154,7 @@ func TestGetConfigType(t *testing.T) {
definitionAlias: "Def2",
},
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definition.UserPrefix + "catalog.config.oam.dev": types.VelaCoreConfig,
},
},
}
@@ -285,37 +289,53 @@ func TestGetConfigs(t *testing.T) {
s := runtime.NewScheme()
v1beta1.AddToScheme(s)
corev1.AddToScheme(s)
def1 := &v1beta1.ComponentDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "ComponentDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
terraformapi.AddToScheme(s)
createdTime, _ := time.Parse(time.UnixDate, "Wed Apr 7 11:06:39 PST 2022")
provider1 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "def1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definitionType: types.TerraformProvider,
},
Name: "provider1",
Namespace: "default",
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: terraformapi.ProviderStatus{
State: terraformtypes.ProviderIsReady,
},
}
def2 := &v1beta1.ComponentDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "ComponentDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
provider2 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "def2",
Namespace: types.DefaultKubeVelaNS,
Annotations: map[string]string{
definitionAlias: "Def2",
},
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
},
Name: "provider2",
Namespace: "default",
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: terraformapi.ProviderStatus{
State: terraformtypes.ProviderIsNotReady,
},
}
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(def1, def2).Build()
provider3 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "provider3",
Namespace: "default",
},
}
app1 := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "provider3",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigType: "terraform-alibaba",
},
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: common.AppStatus{
Phase: common.ApplicationRendering,
},
}
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(provider1, provider2, provider3, app1).Build()
h := &configUseCaseImpl{kubeClient: k8sClient}
@@ -343,7 +363,25 @@ func TestGetConfigs(t *testing.T) {
h: h,
},
want: want{
configs: nil,
configs: []*apis.Config{
{
Name: "provider1",
CreatedTime: &createdTime,
Status: "Ready",
},
{
Name: "provider2",
CreatedTime: &createdTime,
Status: "Not ready",
},
{
Name: "provider3",
CreatedTime: &createdTime,
Status: "Not ready",
ConfigType: "terraform-alibaba",
ApplicationStatus: common.ApplicationRendering,
},
},
},
},
}
@@ -519,7 +557,7 @@ func TestSyncConfigs(t *testing.T) {
Name: "s1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigProject: "p1",
types.LabelConfigSyncToMultiCluster: "true",
},
@@ -604,3 +642,129 @@ func TestSyncConfigs(t *testing.T) {
})
}
}
func TestDeleteConfig(t *testing.T) {
s := runtime.NewScheme()
v1beta1.AddToScheme(s)
corev1.AddToScheme(s)
terraformapi.AddToScheme(s)
provider1 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "p1",
Namespace: "default",
},
}
provider2 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "p2",
Namespace: "default",
},
}
provider3 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "p3",
Namespace: "default",
},
}
app1 := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "config-terraform-provider-p1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigType: "terraform-alibaba",
},
},
}
app2 := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "p2",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigType: "terraform-alibaba",
},
},
}
normalApp := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "a9",
Namespace: types.DefaultKubeVelaNS,
},
}
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(provider1, provider2, provider3, app1, app2, normalApp).Build()
h := &configUseCaseImpl{kubeClient: k8sClient}
type args struct {
configType string
name string
h ConfigHandler
}
type want struct {
errMsg string
}
ctx := context.Background()
testcases := []struct {
name string
args args
want want
}{
{
name: "delete a legacy terraform provider",
args: args{
configType: "terraform-alibaba",
name: "p1",
h: h,
},
want: want{},
},
{
name: "delete a terraform provider",
args: args{
configType: "terraform-alibaba",
name: "p2",
h: h,
},
want: want{},
},
{
name: "delete a terraform provider, but its application not found",
args: args{
configType: "terraform-alibaba",
name: "p3",
h: h,
},
want: want{
errMsg: "could not be disabled because it was created by enabling a Terraform provider or was manually created",
},
},
{
name: "delete a normal config, but failed",
args: args{
configType: "config-image-registry",
name: "a10",
h: h,
},
want: want{
errMsg: "not found",
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
err := tc.args.h.DeleteConfig(ctx, tc.args.configType, tc.args.name)
if tc.want.errMsg != "" || err != nil {
assert.ErrorContains(t, err, tc.want.errMsg)
}
})
}
}

View File

@@ -20,6 +20,8 @@ import (
"context"
"strconv"
"github.com/oam-dev/kubevela/pkg/utils/config"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
"github.com/oam-dev/kubevela/pkg/apiserver/log"
@@ -31,7 +33,6 @@ import (
"github.com/oam-dev/kubevela/pkg/utils/helm"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types2 "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -133,41 +134,20 @@ func (d defaultHelmHandler) ListChartRepo(ctx context.Context, projectName strin
var res []*v1.ChartRepoResponse
var err error
if len(projectName) != 0 {
projectSecrets := corev1.SecretList{}
opts := []client.ListOption{
client.MatchingLabels{oam.LabelConfigType: "config-helm-repository", types.LabelConfigProject: projectName},
client.InNamespace(types.DefaultKubeVelaNS),
}
err = d.k8sClient.List(ctx, &projectSecrets, opts...)
if err != nil {
return nil, err
}
for _, item := range projectSecrets.Items {
res = append(res, &v1.ChartRepoResponse{URL: string(item.Data["url"]), SecretName: item.Name})
}
projectSecrets := corev1.SecretList{}
opts := []client.ListOption{
client.MatchingLabels{oam.LabelConfigType: "config-helm-repository"},
client.InNamespace(types.DefaultKubeVelaNS),
}
globalSecrets := corev1.SecretList{}
selector := metav1.LabelSelector{
MatchLabels: map[string]string{oam.LabelConfigType: "config-helm-repository"},
MatchExpressions: []metav1.LabelSelectorRequirement{
{Key: types.LabelConfigProject, Operator: metav1.LabelSelectorOpDoesNotExist},
},
}
ls, _ := metav1.LabelSelectorAsSelector(&selector)
err = d.k8sClient.List(ctx, &globalSecrets, &client.ListOptions{
LabelSelector: ls,
Namespace: types.DefaultKubeVelaNS,
})
err = d.k8sClient.List(ctx, &projectSecrets, opts...)
if err != nil {
return nil, err
}
for _, item := range globalSecrets.Items {
res = append(res, &v1.ChartRepoResponse{URL: string(item.Data["url"]), SecretName: item.Name})
for _, item := range projectSecrets.Items {
if config.ProjectMatched(item.DeepCopy(), projectName) {
res = append(res, &v1.ChartRepoResponse{URL: string(item.Data["url"]), SecretName: item.Name})
}
}
return &v1.ChartRepoResponseList{ChartRepoResponse: res}, nil

View File

@@ -338,6 +338,7 @@ kind: Secret
metadata:
labels:
config.oam.dev/type: config-helm-repository
config.oam.dev/project: ""
name: global-helm-repo
namespace: vela-system
type: Opaque

View File

@@ -487,7 +487,7 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
if err := p.k8sClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
}); err != nil {
return nil, err
}
@@ -499,7 +499,7 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
return nil, err
}
for _, p := range providers.Items {
if p.Labels[types.LabelConfigCatalog] == velaCoreConfig {
if p.Labels[types.LabelConfigCatalog] == types.VelaCoreConfig {
continue
}
t := p.CreationTimestamp.Time
@@ -520,7 +520,7 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
for _, a := range apps.Items {
appProject := a.Labels[types.LabelConfigProject]
if a.Status.Phase != common.ApplicationRunning || (appProject != "" && appProject != projectName) ||
!strings.Contains(a.Labels[types.LabelConfigType], types.TerrfaormComponentPrefix) {
!strings.Contains(a.Labels[types.LabelConfigType], types.TerraformComponentPrefix) {
continue
}
configs = append(configs, retrieveConfigFromApplication(a, appProject))
@@ -561,13 +561,6 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
configs[i].ConfigTypeAlias = d.Annotations[definitionAlias]
}
}
if c.ApplicationStatus != "" {
if c.ApplicationStatus == common.ApplicationRunning {
configs[i].Status = configIsReady
} else {
configs[i].Status = configIsNotReady
}
}
}
return configs, nil
}
@@ -600,12 +593,22 @@ func ConvertProjectUserModel2Base(user *model.ProjectUser) *apisv1.ProjectUserBa
}
func retrieveConfigFromApplication(a v1beta1.Application, project string) *apisv1.Config {
var (
applicationStatus = a.Status.Phase
status string
)
if applicationStatus == common.ApplicationRunning {
status = configIsReady
} else {
status = configIsNotReady
}
return &apisv1.Config{
ConfigType: a.Labels[types.LabelConfigType],
Name: a.Name,
Project: project,
CreatedTime: &(a.CreationTimestamp.Time),
ApplicationStatus: a.Status.Phase,
ApplicationStatus: applicationStatus,
Status: status,
Alias: a.Annotations[types.AnnotationConfigAlias],
Description: a.Annotations[types.AnnotationConfigDescription],
}

View File

@@ -293,7 +293,7 @@ func TestProjectGetConfigs(t *testing.T) {
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
velatypes.LabelConfigCatalog: velaCoreConfig,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: "terraform-provider",
"config.oam.dev/project": "p1",
},
@@ -308,7 +308,7 @@ func TestProjectGetConfigs(t *testing.T) {
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
velatypes.LabelConfigCatalog: velaCoreConfig,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: "terraform-provider",
},
CreationTimestamp: metav1.NewTime(createdTime),
@@ -322,7 +322,7 @@ func TestProjectGetConfigs(t *testing.T) {
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
velatypes.LabelConfigCatalog: velaCoreConfig,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: "dex-connector",
"config.oam.dev/project": "p3",
},
@@ -347,7 +347,7 @@ func TestProjectGetConfigs(t *testing.T) {
Name: "provider2",
Namespace: "default",
Labels: map[string]string{
velatypes.LabelConfigCatalog: velaCoreConfig,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
},
},
Status: terraformapi.ProviderStatus{

View File

@@ -27,7 +27,7 @@ import (
"cuelang.org/go/cue/format"
json2cue "cuelang.org/go/encoding/json"
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
@@ -63,7 +63,9 @@ const (
// WriteConnectionSecretToRefKey is used to create a secret for cloud resource connection
WriteConnectionSecretToRefKey = "writeConnectionSecretToRef"
// RegionKey is the region of a Cloud Provider
RegionKey = "region"
// It's used to override the region of a Cloud Provider
// Refer to https://github.com/oam-dev/terraform-controller/blob/master/api/v1beta2/configuration_types.go#L66 for details
RegionKey = "customRegion"
// ProviderRefKey is the reference of a Provider
ProviderRefKey = "providerRef"
)
@@ -666,7 +668,7 @@ func generateTerraformConfigurationWorkload(wl *Workload, ns string) (*unstructu
}
configuration := terraformapi.Configuration{
TypeMeta: metav1.TypeMeta{APIVersion: "terraform.core.oam.dev/v1beta1", Kind: "Configuration"},
TypeMeta: metav1.TypeMeta{APIVersion: "terraform.core.oam.dev/v1beta2", Kind: "Configuration"},
ObjectMeta: metav1.ObjectMeta{
Name: wl.Name,
Namespace: ns,
@@ -679,8 +681,6 @@ func generateTerraformConfigurationWorkload(wl *Workload, ns string) (*unstructu
switch wl.FullTemplate.Terraform.Type {
case "hcl":
configuration.Spec.HCL = wl.FullTemplate.Terraform.Configuration
case "json":
configuration.Spec.JSON = wl.FullTemplate.Terraform.Configuration
case "remote":
configuration.Spec.Remote = wl.FullTemplate.Terraform.Configuration
configuration.Spec.Path = wl.FullTemplate.Terraform.Path

View File

@@ -30,7 +30,7 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/google/go-cmp/cmp"
terraformtypes "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
"github.com/pkg/errors"
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -584,10 +584,6 @@ variable "password" {
raw.Raw = data
workload := terraformapi.Configuration{
TypeMeta: metav1.TypeMeta{
APIVersion: "terraform.core.oam.dev/v1beta1",
Kind: "Configuration",
},
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app.oam.dev/appRevision": "v1",
@@ -902,7 +898,6 @@ func TestGenerateTerraformConfigurationWorkload(t *testing.T) {
type args struct {
writeConnectionSecretToRef *terraformtypes.SecretReference
json string
hcl string
remote string
params map[string]interface{}
@@ -917,16 +912,6 @@ func TestGenerateTerraformConfigurationWorkload(t *testing.T) {
args args
want want
}{
"json workload with secret": {
args: args{
json: "abc",
params: map[string]interface{}{"acl": "private",
"writeConnectionSecretToRef": map[string]interface{}{"name": "oss", "namespace": ""}},
writeConnectionSecretToRef: &terraformtypes.SecretReference{Name: "oss", Namespace: "default"},
},
want: want{err: nil}},
"valid hcl workload": {
args: args{
hcl: "abc",
@@ -999,19 +984,6 @@ func TestGenerateTerraformConfigurationWorkload(t *testing.T) {
}
configSpec.WriteConnectionSecretToReference = tc.args.writeConnectionSecretToRef
}
if tc.args.json != "" {
template = &Template{
Terraform: &common.Terraform{
Configuration: tc.args.json,
Type: "json",
},
}
configSpec = terraformapi.ConfigurationSpec{
JSON: tc.args.json,
Variable: raw,
}
configSpec.WriteConnectionSecretToReference = tc.args.writeConnectionSecretToRef
}
if tc.args.remote != "" {
template = &Template{
Terraform: &common.Terraform{
@@ -1025,7 +997,7 @@ func TestGenerateTerraformConfigurationWorkload(t *testing.T) {
}
configSpec.WriteConnectionSecretToReference = tc.args.writeConnectionSecretToRef
}
if tc.args.hcl == "" && tc.args.json == "" && tc.args.remote == "" {
if tc.args.hcl == "" && tc.args.remote == "" {
template = &Template{
Terraform: &common.Terraform{},
}
@@ -1061,7 +1033,7 @@ func TestGenerateTerraformConfigurationWorkload(t *testing.T) {
if err == nil {
tfConfiguration := terraformapi.Configuration{
TypeMeta: metav1.TypeMeta{APIVersion: "terraform.core.oam.dev/v1beta1", Kind: "Configuration"},
TypeMeta: metav1.TypeMeta{APIVersion: "terraform.core.oam.dev/v1beta2", Kind: "Configuration"},
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns},
Spec: configSpec,
}

View File

@@ -288,6 +288,30 @@ func (p *Parser) GenerateAppFileFromRevision(appRev *v1beta1.ApplicationRevision
for k, v := range appRev.Spec.WorkflowStepDefinitions {
appfile.RelatedWorkflowStepDefinitions[k] = v.DeepCopy()
}
// add compatible code for upgrading to v1.3 as the workflow steps were not recorded before v1.2
if len(appfile.RelatedWorkflowStepDefinitions) == 0 && len(appfile.WorkflowSteps) > 0 {
ctx := context.Background()
for _, workflowStep := range appfile.WorkflowSteps {
if wftypes.IsBuiltinWorkflowStepType(workflowStep.Type) {
continue
}
if _, found := appfile.RelatedWorkflowStepDefinitions[workflowStep.Type]; found {
continue
}
def := &v1beta1.WorkflowStepDefinition{}
if err := util.GetCapabilityDefinition(ctx, p.client, def, workflowStep.Type); err != nil {
return nil, errors.Wrapf(err, "failed to get workflow step definition %s", workflowStep.Type)
}
appfile.RelatedWorkflowStepDefinitions[workflowStep.Type] = def
}
appRev.Spec.WorkflowStepDefinitions = make(map[string]v1beta1.WorkflowStepDefinition)
for name, def := range appfile.RelatedWorkflowStepDefinitions {
appRev.Spec.WorkflowStepDefinitions[name] = *def
}
}
for k, v := range appRev.Spec.ScopeDefinitions {
appfile.RelatedScopeDefinitions[k] = v.DeepCopy()
}

View File

@@ -22,6 +22,8 @@ import (
"reflect"
"strings"
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/google/go-cmp/cmp"
"github.com/crossplane/crossplane-runtime/pkg/test"
@@ -506,3 +508,202 @@ var _ = Describe("Test appFile parser", func() {
})
})
var _ = Describe("Test application parser", func() {
var app v1beta1.Application
var apprev v1beta1.ApplicationRevision
var wsd v1beta1.WorkflowStepDefinition
var expectedExceptAppfile *Appfile
var mockClient test.MockClient
BeforeEach(func() {
// prepare WorkflowStepDefinition
Expect(common2.ReadYamlToObject("testdata/backport-1-2/wsd.yaml", &wsd)).Should(BeNil())
// prepare verify data
expectedExceptAppfile = &Appfile{
Name: "backport-1-2-test-demo",
Workloads: []*Workload{
{
Name: "backport-1-2-test-demo",
Type: "webservice",
Params: map[string]interface{}{
"image": "nginx",
},
FullTemplate: &Template{
TemplateStr: `
output: {
apiVersion: "apps/v1"
kind: "Deployment"
spec: {
selector: matchLabels: {
"app.oam.dev/component": context.name
}
template: {
metadata: labels: {
"app.oam.dev/component": context.name
}
spec: {
containers: [{
name: context.name
image: parameter.image
if parameter["cmd"] != _|_ {
command: parameter.cmd
}
}]
}
}
selector:
matchLabels:
"app.oam.dev/component": context.name
}
}
parameter: {
// +usage=Which image would you like to use for your service
// +short=i
image: string
cmd?: [...string]
}`,
},
Traits: []*Trait{
{
Name: "scaler",
Params: map[string]interface{}{
"replicas": float64(1),
},
Template: `
parameter: {
// +usage=Specify the number of workload
replicas: *1 | int
}
// +patchStrategy=retainKeys
patch: spec: replicas: parameter.replicas
`,
},
},
},
},
WorkflowSteps: []v1beta1.WorkflowStep{
{
Name: "apply",
Type: "apply-application",
},
},
}
// Create mock client
mockClient = test.MockClient{
MockGet: func(ctx context.Context, key client.ObjectKey, obj client.Object) error {
if strings.Contains(key.Name, "unknown") {
return &errors2.StatusError{ErrStatus: metav1.Status{Reason: "NotFound", Message: "not found"}}
}
switch o := obj.(type) {
case *v1beta1.ComponentDefinition:
wd, err := util.UnMarshalStringToComponentDefinition(componenetDefinition)
if err != nil {
return err
}
*o = *wd
case *v1beta1.TraitDefinition:
td, err := util.UnMarshalStringToTraitDefinition(traitDefinition)
if err != nil {
return err
}
*o = *td
case *v1beta1.WorkflowStepDefinition:
*o = wsd
case *v1beta1.ApplicationRevision:
*o = apprev
default:
// skip
}
return nil
},
}
})
When("with apply-application workflowStep", func() {
BeforeEach(func() {
// prepare application
Expect(common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app)).Should(BeNil())
// prepare application revision
Expect(common2.ReadYamlToObject("testdata/backport-1-2/apprev1.yaml", &apprev)).Should(BeNil())
})
It("Test we can parse an application revision to an appFile 1", func() {
appfile, err := NewApplicationParser(&mockClient, dm, pd).GenerateAppFile(context.TODO(), &app)
Expect(err).ShouldNot(HaveOccurred())
Expect(equal(expectedExceptAppfile, appfile)).Should(BeTrue())
Expect(len(appfile.WorkflowSteps) > 0 &&
len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions)).Should(BeTrue())
Expect(len(appfile.WorkflowSteps) > 0 && func() bool {
this := appfile.RelatedWorkflowStepDefinitions
that := appfile.AppRevision.Spec.WorkflowStepDefinitions
for i, w := range this {
thatW := that[i]
if !reflect.DeepEqual(*w, thatW) {
fmt.Printf("appfile wsd:%s apprev wsd%s", (*w).Name, thatW.Name)
return false
}
}
return true
}()).Should(BeTrue())
})
})
When("with apply-application and apply-component build-in workflowStep", func() {
BeforeEach(func() {
// prepare application
Expect(common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app)).Should(BeNil())
// prepare application revision
Expect(common2.ReadYamlToObject("testdata/backport-1-2/apprev2.yaml", &apprev)).Should(BeNil())
})
It("Test we can parse an application revision to an appFile 2", func() {
appfile, err := NewApplicationParser(&mockClient, dm, pd).GenerateAppFile(context.TODO(), &app)
Expect(err).ShouldNot(HaveOccurred())
Expect(equal(expectedExceptAppfile, appfile)).Should(BeTrue())
Expect(len(appfile.WorkflowSteps) > 0 &&
len(appfile.RelatedWorkflowStepDefinitions) == len(appfile.AppRevision.Spec.WorkflowStepDefinitions)).Should(BeTrue())
Expect(len(appfile.WorkflowSteps) > 0 && func() bool {
this := appfile.RelatedWorkflowStepDefinitions
that := appfile.AppRevision.Spec.WorkflowStepDefinitions
for i, w := range this {
thatW := that[i]
if !reflect.DeepEqual(*w, thatW) {
fmt.Printf("appfile wsd:%s apprev wsd%s", (*w).Name, thatW.Name)
return false
}
}
return true
}()).Should(BeTrue())
})
})
When("with unknown workflowStep", func() {
BeforeEach(func() {
// prepare application
Expect(common2.ReadYamlToObject("testdata/backport-1-2/app.yaml", &app)).Should(BeNil())
// prepare application revision
Expect(common2.ReadYamlToObject("testdata/backport-1-2/apprev3.yaml", &apprev)).Should(BeNil())
})
It("Test we can parse an application revision to an appFile 3", func() {
_, err := NewApplicationParser(&mockClient, dm, pd).GenerateAppFile(context.TODO(), &app)
Expect(err).Should(HaveOccurred())
Expect(err.Error() == "failed to get workflow step definition apply-application-unknown: not found").Should(BeTrue())
})
})
})

View File

@@ -0,0 +1,26 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
annotations:
app.oam.dev/publishVersion: workflow-default-123456
name: backport-1-2-test-demo
namespace: default
spec:
components:
- name: backport-1-2-test-demo
properties:
image: nginx
traits:
- properties:
replicas: 1
type: scaler
type: webservice
workflow:
steps:
- name: apply
type: apply-application
status:
latestRevision:
name: backport-1-2-test-demo-v1
revision: 1
revisionHash: 38ddf4e721073703

View File

@@ -0,0 +1,271 @@
apiVersion: core.oam.dev/v1beta1
kind: ApplicationRevision
metadata:
annotations:
app.oam.dev/publishVersion: workflow-default-123456
name: backport-1-2-test-demo-v1
namespace: default
spec:
application:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
annotations:
app.oam.dev/publishVersion: workflow-default-123456
name: backport-1-2-test-demo
namespace: default
spec:
components:
- name: backport-1-2-test-demo
properties:
image: nginx
traits:
- properties:
replicas: 1
type: scaler
type: webservice
workflow:
steps:
- name: apply
type: apply-application
status: {}
componentDefinitions:
webservice:
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations:
definition.oam.dev/description: Describes long-running, scalable, containerized
services that have a stable network endpoint to receive external network
traffic from customers.
meta.helm.sh/release-name: kubevela
meta.helm.sh/release-namespace: vela-system
labels:
app.kubernetes.io/managed-by: Helm
name: webservice
namespace: vela-system
spec:
schematic:
cue:
template: "import (\n\t\"strconv\"\n)\n\nmountsArray: {\n\tpvc: *[\n\t\tfor
v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tname:
\ v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tconfigMap: *[\n\t\t\tfor
v in parameter.volumeMounts.configMap {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tsecret:
*[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\temptyDir:
*[\n\t\t\tfor v in parameter.volumeMounts.emptyDir {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\thostPath:
*[\n\t\t\tfor v in parameter.volumeMounts.hostPath {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n}\nvolumesArray:
{\n\tpvc: *[\n\t\tfor v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tname:
v.name\n\t\t\t\tpersistentVolumeClaim: claimName: v.claimName\n\t\t\t}\n\t\t},\n\t]
| []\n\n\tconfigMap: *[\n\t\t\tfor v in parameter.volumeMounts.configMap
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\tconfigMap: {\n\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\tname: v.cmName\n\t\t\t\t\tif v.items
!= _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
| []\n\n\tsecret: *[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tname:
v.name\n\t\t\t\tsecret: {\n\t\t\t\t\tdefaultMode: v.defaultMode\n\t\t\t\t\tsecretName:
\ v.secretName\n\t\t\t\t\tif v.items != _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
| []\n\n\temptyDir: *[\n\t\t\tfor v in parameter.volumeMounts.emptyDir
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\temptyDir: medium: v.medium\n\t\t\t}\n\t\t},\n\t]
| []\n\n\thostPath: *[\n\t\t\tfor v in parameter.volumeMounts.hostPath
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\thostPath: path: v.path\n\t\t\t}\n\t\t},\n\t]
| []\n}\noutput: {\n\tapiVersion: \"apps/v1\"\n\tkind: \"Deployment\"\n\tspec:
{\n\t\tselector: matchLabels: \"app.oam.dev/component\": context.name\n\n\t\ttemplate:
{\n\t\t\tmetadata: {\n\t\t\t\tlabels: {\n\t\t\t\t\tif parameter.labels
!= _|_ {\n\t\t\t\t\t\tparameter.labels\n\t\t\t\t\t}\n\t\t\t\t\tif parameter.addRevisionLabel
{\n\t\t\t\t\t\t\"app.oam.dev/revision\": context.revision\n\t\t\t\t\t}\n\t\t\t\t\t\"app.oam.dev/component\":
context.name\n\t\t\t\t}\n\t\t\t\tif parameter.annotations != _|_ {\n\t\t\t\t\tannotations:
parameter.annotations\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspec: {\n\t\t\t\tcontainers:
[{\n\t\t\t\t\tname: context.name\n\t\t\t\t\timage: parameter.image\n\t\t\t\t\tif
parameter[\"port\"] != _|_ && parameter[\"ports\"] == _|_ {\n\t\t\t\t\t\tports:
[{\n\t\t\t\t\t\t\tcontainerPort: parameter.port\n\t\t\t\t\t\t}]\n\t\t\t\t\t}\n\t\t\t\t\tif
parameter[\"ports\"] != _|_ {\n\t\t\t\t\t\tports: [ for v in parameter.ports
{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontainerPort: v.port\n\t\t\t\t\t\t\t\tprotocol:
\ v.protocol\n\t\t\t\t\t\t\t\tif v.name != _|_ {\n\t\t\t\t\t\t\t\t\tname:
v.name\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif v.name == _|_ {\n\t\t\t\t\t\t\t\t\tname:
\"port-\" + strconv.FormatInt(v.port, 10)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"imagePullPolicy\"] != _|_ {\n\t\t\t\t\t\timagePullPolicy:
parameter.imagePullPolicy\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"cmd\"]
!= _|_ {\n\t\t\t\t\t\tcommand: parameter.cmd\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"env\"] != _|_ {\n\t\t\t\t\t\tenv: parameter.env\n\t\t\t\t\t}\n\n\t\t\t\t\tif
context[\"config\"] != _|_ {\n\t\t\t\t\t\tenv: context.config\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"cpu\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
cpu: parameter.cpu\n\t\t\t\t\t\t\trequests: cpu: parameter.cpu\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"memory\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
memory: parameter.memory\n\t\t\t\t\t\t\trequests: memory: parameter.memory\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
{\n\t\t\t\t\t\tvolumeMounts: [ for v in parameter.volumes {\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmountPath:
v.mountPath\n\t\t\t\t\t\t\t\tname: v.name\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\t\tvolumeMounts: mountsArray.pvc
+ mountsArray.configMap + mountsArray.secret + mountsArray.emptyDir
+ mountsArray.hostPath\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"livenessProbe\"]
!= _|_ {\n\t\t\t\t\t\tlivenessProbe: parameter.livenessProbe\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"readinessProbe\"] != _|_ {\n\t\t\t\t\t\treadinessProbe:
parameter.readinessProbe\n\t\t\t\t\t}\n\n\t\t\t\t}]\n\n\t\t\t\tif parameter[\"hostAliases\"]
!= _|_ {\n\t\t\t\t\t// +patchKey=ip\n\t\t\t\t\thostAliases: parameter.hostAliases\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"imagePullSecrets\"] != _|_ {\n\t\t\t\t\timagePullSecrets:
[ for v in parameter.imagePullSecrets {\n\t\t\t\t\t\tname: v\n\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
{\n\t\t\t\t\tvolumes: [ for v in parameter.volumes {\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname:
v.name\n\t\t\t\t\t\t\tif v.type == \"pvc\" {\n\t\t\t\t\t\t\t\tpersistentVolumeClaim:
claimName: v.claimName\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif v.type ==
\"configMap\" {\n\t\t\t\t\t\t\t\tconfigMap: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\t\t\t\t\tname: v.cmName\n\t\t\t\t\t\t\t\t\tif
v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
v.type == \"secret\" {\n\t\t\t\t\t\t\t\tsecret: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\t\t\t\t\tsecretName: v.secretName\n\t\t\t\t\t\t\t\t\tif
v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
v.type == \"emptyDir\" {\n\t\t\t\t\t\t\t\temptyDir: medium: v.medium\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}]\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\tvolumes: volumesArray.pvc
+ volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir
+ volumesArray.hostPath\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nexposePorts:
[\n\tfor v in parameter.ports if v.expose == true {\n\t\tport: v.port\n\t\ttargetPort:
v.port\n\t\tif v.name != _|_ {\n\t\t\tname: v.name\n\t\t}\n\t\tif v.name
== _|_ {\n\t\t\tname: \"port-\" + strconv.FormatInt(v.port, 10)\n\t\t}\n\t},\n]\noutputs:
{\n\tif len(exposePorts) != 0 {\n\t\twebserviceExpose: {\n\t\t\tapiVersion:
\"v1\"\n\t\t\tkind: \"Service\"\n\t\t\tmetadata: name: context.name\n\t\t\tspec:
{\n\t\t\t\tselector: \"app.oam.dev/component\": context.name\n\t\t\t\tports:
exposePorts\n\t\t\t\ttype: parameter.exposeType\n\t\t\t}\n\t\t}\n\t}\n}\nparameter:
{\n\t// +usage=Specify the labels in the workload\n\tlabels?: [string]:
string\n\n\t// +usage=Specify the annotations in the workload\n\tannotations?:
[string]: string\n\n\t// +usage=Which image would you like to use for
your service\n\t// +short=i\n\timage: string\n\n\t// +usage=Specify
image pull policy for your service\n\timagePullPolicy?: \"Always\" |
\"Never\" | \"IfNotPresent\"\n\n\t// +usage=Specify image pull secrets
for your service\n\timagePullSecrets?: [...string]\n\n\t// +ignore\n\t//
+usage=Deprecated field, please use ports instead\n\t// +short=p\n\tport?:
int\n\n\t// +usage=Which ports do you want customer traffic sent to,
defaults to 80\n\tports?: [...{\n\t\t// +usage=Number of port to expose
on the pod's IP address\n\t\tport: int\n\t\t// +usage=Name of the port\n\t\tname?:
string\n\t\t// +usage=Protocol for port. Must be UDP, TCP, or SCTP\n\t\tprotocol:
*\"TCP\" | \"UDP\" | \"SCTP\"\n\t\t// +usage=Specify if the port should
be exposed\n\t\texpose: *false | bool\n\t}]\n\n\t// +ignore\n\t// +usage=Specify
what kind of Service you want. options: \"ClusterIP\", \"NodePort\",
\"LoadBalancer\", \"ExternalName\"\n\texposeType: *\"ClusterIP\" | \"NodePort\"
| \"LoadBalancer\" | \"ExternalName\"\n\n\t// +ignore\n\t// +usage=If
addRevisionLabel is true, the revision label will be added to the underlying
pods\n\taddRevisionLabel: *false | bool\n\n\t// +usage=Commands to run
in the container\n\tcmd?: [...string]\n\n\t// +usage=Define arguments
by using environment variables\n\tenv?: [...{\n\t\t// +usage=Environment
variable name\n\t\tname: string\n\t\t// +usage=The value of the environment
variable\n\t\tvalue?: string\n\t\t// +usage=Specifies a source the value
of this var should come from\n\t\tvalueFrom?: {\n\t\t\t// +usage=Selects
a key of a secret in the pod's namespace\n\t\t\tsecretKeyRef?: {\n\t\t\t\t//
+usage=The name of the secret in the pod's namespace to select from\n\t\t\t\tname:
string\n\t\t\t\t// +usage=The key of the secret to select from. Must
be a valid secret key\n\t\t\t\tkey: string\n\t\t\t}\n\t\t\t// +usage=Selects
a key of a config map in the pod's namespace\n\t\t\tconfigMapKeyRef?:
{\n\t\t\t\t// +usage=The name of the config map in the pod's namespace
to select from\n\t\t\t\tname: string\n\t\t\t\t// +usage=The key of the
config map to select from. Must be a valid secret key\n\t\t\t\tkey:
string\n\t\t\t}\n\t\t}\n\t}]\n\n\t// +usage=Number of CPU units for
the service, like \n\tcpu?: string\n\n\t//
+usage=Specifies the attributes of the memory resource required for
the container.\n\tmemory?: string\n\n\tvolumeMounts?: {\n\t\t// +usage=Mount
PVC type volume\n\t\tpvc?: [...{\n\t\t\tname: string\n\t\t\tmountPath:
string\n\t\t\t// +usage=The name of the PVC\n\t\t\tclaimName: string\n\t\t}]\n\t\t//
+usage=Mount ConfigMap type volume\n\t\tconfigMap?: [...{\n\t\t\tname:
\ string\n\t\t\tmountPath: string\n\t\t\tdefaultMode: *420 |
int\n\t\t\tcmName: string\n\t\t\titems?: [...{\n\t\t\t\tkey: string\n\t\t\t\tpath:
string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount
Secret type volume\n\t\tsecret?: [...{\n\t\t\tname: string\n\t\t\tmountPath:
\ string\n\t\t\tdefaultMode: *420 | int\n\t\t\tsecretName: string\n\t\t\titems?:
[...{\n\t\t\t\tkey: string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
| int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount EmptyDir type volume\n\t\temptyDir?:
[...{\n\t\t\tname: string\n\t\t\tmountPath: string\n\t\t\tmedium:
\ *\"\" | \"Memory\"\n\t\t}]\n\t\t// +usage=Mount HostPath type volume\n\t\thostPath?:
[...{\n\t\t\tname: string\n\t\t\tmountPath: string\n\t\t\tpath:
\ string\n\t\t}]\n\t}\n\n\t// +usage=Deprecated field, use volumeMounts
instead.\n\tvolumes?: [...{\n\t\tname: string\n\t\tmountPath: string\n\t\t//
+usage=Specify volume type, options: \"pvc\",\"configMap\",\"secret\",\"emptyDir\"\n\t\ttype:
\"pvc\" | \"configMap\" | \"secret\" | \"emptyDir\"\n\t\tif type ==
\"pvc\" {\n\t\t\tclaimName: string\n\t\t}\n\t\tif type == \"configMap\"
{\n\t\t\tdefaultMode: *420 | int\n\t\t\tcmName: string\n\t\t\titems?:
[...{\n\t\t\t\tkey: string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
| int\n\t\t\t}]\n\t\t}\n\t\tif type == \"secret\" {\n\t\t\tdefaultMode:
*420 | int\n\t\t\tsecretName: string\n\t\t\titems?: [...{\n\t\t\t\tkey:
\ string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}\n\t\tif
type == \"emptyDir\" {\n\t\t\tmedium: *\"\" | \"Memory\"\n\t\t}\n\t}]\n\n\t//
+usage=Instructions for assessing whether the container is alive.\n\tlivenessProbe?:
#HealthProbe\n\n\t// +usage=Instructions for assessing whether the container
is in a suitable state to serve traffic.\n\treadinessProbe?: #HealthProbe\n\n\t//
+usage=Specify the hostAliases to add\n\thostAliases?: [...{\n\t\tip:
string\n\t\thostnames: [...string]\n\t}]\n}\n#HealthProbe: {\n\n\t//
+usage=Instructions for assessing container health by executing a command.
Either this attribute or the httpGet attribute or the tcpSocket attribute
MUST be specified. This attribute is mutually exclusive with both the
httpGet attribute and the tcpSocket attribute.\n\texec?: {\n\t\t// +usage=A
command to be executed inside the container to assess its health. Each
space delimited token of the command is a separate array element. Commands
exiting 0 are considered to be successful probes, whilst all other exit
codes are considered failures.\n\t\tcommand: [...string]\n\t}\n\n\t//
+usage=Instructions for assessing container health by executing an HTTP
GET request. Either this attribute or the exec attribute or the tcpSocket
attribute MUST be specified. This attribute is mutually exclusive with
both the exec attribute and the tcpSocket attribute.\n\thttpGet?: {\n\t\t//
+usage=The endpoint, relative to the port, to which the HTTP GET request
should be directed.\n\t\tpath: string\n\t\t// +usage=The TCP socket
within the container to which the HTTP GET request should be directed.\n\t\tport:
int\n\t\thttpHeaders?: [...{\n\t\t\tname: string\n\t\t\tvalue: string\n\t\t}]\n\t}\n\n\t//
+usage=Instructions for assessing container health by probing a TCP
socket. Either this attribute or the exec attribute or the httpGet attribute
MUST be specified. This attribute is mutually exclusive with both the
exec attribute and the httpGet attribute.\n\ttcpSocket?: {\n\t\t// +usage=The
TCP socket within the container that should be probed to assess container
health.\n\t\tport: int\n\t}\n\n\t// +usage=Number of seconds after the
container is started before the first probe is initiated.\n\tinitialDelaySeconds:
*0 | int\n\n\t// +usage=How often, in seconds, to execute the probe.\n\tperiodSeconds:
*10 | int\n\n\t// +usage=Number of seconds after which the probe times
out.\n\ttimeoutSeconds: *1 | int\n\n\t// +usage=Minimum consecutive
successes for the probe to be considered successful after having failed.\n\tsuccessThreshold:
*1 | int\n\n\t// +usage=Number of consecutive failures required to determine
the container is not alive (liveness probe) or not ready (readiness
probe).\n\tfailureThreshold: *3 | int\n}\n"
status:
customStatus: "ready: {\n\treadyReplicas: *0 | int\n} & {\n\tif context.output.status.readyReplicas
!= _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n}\nmessage:
\"Ready:\\(ready.readyReplicas)/\\(context.output.spec.replicas)\""
healthPolicy: "ready: {\n\tupdatedReplicas: *0 | int\n\treadyReplicas:
\ *0 | int\n\treplicas: *0 | int\n\tobservedGeneration:
*0 | int\n} & {\n\tif context.output.status.updatedReplicas != _|_ {\n\t\tupdatedReplicas:
context.output.status.updatedReplicas\n\t}\n\tif context.output.status.readyReplicas
!= _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n\tif
context.output.status.replicas != _|_ {\n\t\treplicas: context.output.status.replicas\n\t}\n\tif
context.output.status.observedGeneration != _|_ {\n\t\tobservedGeneration:
context.output.status.observedGeneration\n\t}\n}\nisHealth: (context.output.spec.replicas
== ready.readyReplicas) && (context.output.spec.replicas == ready.updatedReplicas)
&& (context.output.spec.replicas == ready.replicas) && (ready.observedGeneration
== context.output.metadata.generation || ready.observedGeneration > context.output.metadata.generation)"
workload:
definition:
apiVersion: apps/v1
kind: Deployment
type: deployments.apps
status: {}
traitDefinitions:
scaler:
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: Manually scale K8s pod for your workload
which follows the pod spec in path 'spec.template'.
meta.helm.sh/release-name: kubevela
meta.helm.sh/release-namespace: vela-system
labels:
app.kubernetes.io/managed-by: Helm
name: scaler
namespace: vela-system
spec:
appliesToWorkloads:
- '*'
definitionRef:
name: ""
schematic:
cue:
template: "parameter: {\n\t// +usage=Specify the number of workload\n\treplicas:
*1 | int\n}\n// +patchStrategy=retainKeys\npatch: spec: replicas: parameter.replicas\n"
status: {}
status: {}

View File

@@ -0,0 +1,277 @@
apiVersion: core.oam.dev/v1beta1
kind: ApplicationRevision
metadata:
annotations:
app.oam.dev/publishVersion: workflow-default-123456
name: backport-1-2-test-demo-v1
namespace: default
spec:
application:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
annotations:
app.oam.dev/publishVersion: workflow-default-123456
name: backport-1-2-test-demo
namespace: default
spec:
components:
- name: backport-1-2-test-demo
properties:
image: nginx
traits:
- properties:
replicas: 1
type: scaler
type: webservice
workflow:
steps:
- name: apply-component
type: apply-component
properties:
name: backport-1-2-test-demo
- name: apply1
type: apply-application
- name: apply2
type: apply-application
status: {}
componentDefinitions:
webservice:
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations:
definition.oam.dev/description: Describes long-running, scalable, containerized
services that have a stable network endpoint to receive external network
traffic from customers.
meta.helm.sh/release-name: kubevela
meta.helm.sh/release-namespace: vela-system
labels:
app.kubernetes.io/managed-by: Helm
name: webservice
namespace: vela-system
spec:
schematic:
cue:
template: "import (\n\t\"strconv\"\n)\n\nmountsArray: {\n\tpvc: *[\n\t\tfor
v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tname:
\ v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tconfigMap: *[\n\t\t\tfor
v in parameter.volumeMounts.configMap {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tsecret:
*[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\temptyDir:
*[\n\t\t\tfor v in parameter.volumeMounts.emptyDir {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\thostPath:
*[\n\t\t\tfor v in parameter.volumeMounts.hostPath {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n}\nvolumesArray:
{\n\tpvc: *[\n\t\tfor v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tname:
v.name\n\t\t\t\tpersistentVolumeClaim: claimName: v.claimName\n\t\t\t}\n\t\t},\n\t]
| []\n\n\tconfigMap: *[\n\t\t\tfor v in parameter.volumeMounts.configMap
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\tconfigMap: {\n\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\tname: v.cmName\n\t\t\t\t\tif v.items
!= _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
| []\n\n\tsecret: *[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tname:
v.name\n\t\t\t\tsecret: {\n\t\t\t\t\tdefaultMode: v.defaultMode\n\t\t\t\t\tsecretName:
\ v.secretName\n\t\t\t\t\tif v.items != _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
| []\n\n\temptyDir: *[\n\t\t\tfor v in parameter.volumeMounts.emptyDir
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\temptyDir: medium: v.medium\n\t\t\t}\n\t\t},\n\t]
| []\n\n\thostPath: *[\n\t\t\tfor v in parameter.volumeMounts.hostPath
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\thostPath: path: v.path\n\t\t\t}\n\t\t},\n\t]
| []\n}\noutput: {\n\tapiVersion: \"apps/v1\"\n\tkind: \"Deployment\"\n\tspec:
{\n\t\tselector: matchLabels: \"app.oam.dev/component\": context.name\n\n\t\ttemplate:
{\n\t\t\tmetadata: {\n\t\t\t\tlabels: {\n\t\t\t\t\tif parameter.labels
!= _|_ {\n\t\t\t\t\t\tparameter.labels\n\t\t\t\t\t}\n\t\t\t\t\tif parameter.addRevisionLabel
{\n\t\t\t\t\t\t\"app.oam.dev/revision\": context.revision\n\t\t\t\t\t}\n\t\t\t\t\t\"app.oam.dev/component\":
context.name\n\t\t\t\t}\n\t\t\t\tif parameter.annotations != _|_ {\n\t\t\t\t\tannotations:
parameter.annotations\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspec: {\n\t\t\t\tcontainers:
[{\n\t\t\t\t\tname: context.name\n\t\t\t\t\timage: parameter.image\n\t\t\t\t\tif
parameter[\"port\"] != _|_ && parameter[\"ports\"] == _|_ {\n\t\t\t\t\t\tports:
[{\n\t\t\t\t\t\t\tcontainerPort: parameter.port\n\t\t\t\t\t\t}]\n\t\t\t\t\t}\n\t\t\t\t\tif
parameter[\"ports\"] != _|_ {\n\t\t\t\t\t\tports: [ for v in parameter.ports
{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontainerPort: v.port\n\t\t\t\t\t\t\t\tprotocol:
\ v.protocol\n\t\t\t\t\t\t\t\tif v.name != _|_ {\n\t\t\t\t\t\t\t\t\tname:
v.name\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif v.name == _|_ {\n\t\t\t\t\t\t\t\t\tname:
\"port-\" + strconv.FormatInt(v.port, 10)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"imagePullPolicy\"] != _|_ {\n\t\t\t\t\t\timagePullPolicy:
parameter.imagePullPolicy\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"cmd\"]
!= _|_ {\n\t\t\t\t\t\tcommand: parameter.cmd\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"env\"] != _|_ {\n\t\t\t\t\t\tenv: parameter.env\n\t\t\t\t\t}\n\n\t\t\t\t\tif
context[\"config\"] != _|_ {\n\t\t\t\t\t\tenv: context.config\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"cpu\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
cpu: parameter.cpu\n\t\t\t\t\t\t\trequests: cpu: parameter.cpu\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"memory\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
memory: parameter.memory\n\t\t\t\t\t\t\trequests: memory: parameter.memory\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
{\n\t\t\t\t\t\tvolumeMounts: [ for v in parameter.volumes {\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmountPath:
v.mountPath\n\t\t\t\t\t\t\t\tname: v.name\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\t\tvolumeMounts: mountsArray.pvc
+ mountsArray.configMap + mountsArray.secret + mountsArray.emptyDir
+ mountsArray.hostPath\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"livenessProbe\"]
!= _|_ {\n\t\t\t\t\t\tlivenessProbe: parameter.livenessProbe\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"readinessProbe\"] != _|_ {\n\t\t\t\t\t\treadinessProbe:
parameter.readinessProbe\n\t\t\t\t\t}\n\n\t\t\t\t}]\n\n\t\t\t\tif parameter[\"hostAliases\"]
!= _|_ {\n\t\t\t\t\t// +patchKey=ip\n\t\t\t\t\thostAliases: parameter.hostAliases\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"imagePullSecrets\"] != _|_ {\n\t\t\t\t\timagePullSecrets:
[ for v in parameter.imagePullSecrets {\n\t\t\t\t\t\tname: v\n\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
{\n\t\t\t\t\tvolumes: [ for v in parameter.volumes {\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname:
v.name\n\t\t\t\t\t\t\tif v.type == \"pvc\" {\n\t\t\t\t\t\t\t\tpersistentVolumeClaim:
claimName: v.claimName\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif v.type ==
\"configMap\" {\n\t\t\t\t\t\t\t\tconfigMap: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\t\t\t\t\tname: v.cmName\n\t\t\t\t\t\t\t\t\tif
v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
v.type == \"secret\" {\n\t\t\t\t\t\t\t\tsecret: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\t\t\t\t\tsecretName: v.secretName\n\t\t\t\t\t\t\t\t\tif
v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
v.type == \"emptyDir\" {\n\t\t\t\t\t\t\t\temptyDir: medium: v.medium\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}]\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\tvolumes: volumesArray.pvc
+ volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir
+ volumesArray.hostPath\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nexposePorts:
[\n\tfor v in parameter.ports if v.expose == true {\n\t\tport: v.port\n\t\ttargetPort:
v.port\n\t\tif v.name != _|_ {\n\t\t\tname: v.name\n\t\t}\n\t\tif v.name
== _|_ {\n\t\t\tname: \"port-\" + strconv.FormatInt(v.port, 10)\n\t\t}\n\t},\n]\noutputs:
{\n\tif len(exposePorts) != 0 {\n\t\twebserviceExpose: {\n\t\t\tapiVersion:
\"v1\"\n\t\t\tkind: \"Service\"\n\t\t\tmetadata: name: context.name\n\t\t\tspec:
{\n\t\t\t\tselector: \"app.oam.dev/component\": context.name\n\t\t\t\tports:
exposePorts\n\t\t\t\ttype: parameter.exposeType\n\t\t\t}\n\t\t}\n\t}\n}\nparameter:
{\n\t// +usage=Specify the labels in the workload\n\tlabels?: [string]:
string\n\n\t// +usage=Specify the annotations in the workload\n\tannotations?:
[string]: string\n\n\t// +usage=Which image would you like to use for
your service\n\t// +short=i\n\timage: string\n\n\t// +usage=Specify
image pull policy for your service\n\timagePullPolicy?: \"Always\" |
\"Never\" | \"IfNotPresent\"\n\n\t// +usage=Specify image pull secrets
for your service\n\timagePullSecrets?: [...string]\n\n\t// +ignore\n\t//
+usage=Deprecated field, please use ports instead\n\t// +short=p\n\tport?:
int\n\n\t// +usage=Which ports do you want customer traffic sent to,
defaults to 80\n\tports?: [...{\n\t\t// +usage=Number of port to expose
on the pod's IP address\n\t\tport: int\n\t\t// +usage=Name of the port\n\t\tname?:
string\n\t\t// +usage=Protocol for port. Must be UDP, TCP, or SCTP\n\t\tprotocol:
*\"TCP\" | \"UDP\" | \"SCTP\"\n\t\t// +usage=Specify if the port should
be exposed\n\t\texpose: *false | bool\n\t}]\n\n\t// +ignore\n\t// +usage=Specify
what kind of Service you want. options: \"ClusterIP\", \"NodePort\",
\"LoadBalancer\", \"ExternalName\"\n\texposeType: *\"ClusterIP\" | \"NodePort\"
| \"LoadBalancer\" | \"ExternalName\"\n\n\t// +ignore\n\t// +usage=If
addRevisionLabel is true, the revision label will be added to the underlying
pods\n\taddRevisionLabel: *false | bool\n\n\t// +usage=Commands to run
in the container\n\tcmd?: [...string]\n\n\t// +usage=Define arguments
by using environment variables\n\tenv?: [...{\n\t\t// +usage=Environment
variable name\n\t\tname: string\n\t\t// +usage=The value of the environment
variable\n\t\tvalue?: string\n\t\t// +usage=Specifies a source the value
of this var should come from\n\t\tvalueFrom?: {\n\t\t\t// +usage=Selects
a key of a secret in the pod's namespace\n\t\t\tsecretKeyRef?: {\n\t\t\t\t//
+usage=The name of the secret in the pod's namespace to select from\n\t\t\t\tname:
string\n\t\t\t\t// +usage=The key of the secret to select from. Must
be a valid secret key\n\t\t\t\tkey: string\n\t\t\t}\n\t\t\t// +usage=Selects
a key of a config map in the pod's namespace\n\t\t\tconfigMapKeyRef?:
{\n\t\t\t\t// +usage=The name of the config map in the pod's namespace
to select from\n\t\t\t\tname: string\n\t\t\t\t// +usage=The key of the
config map to select from. Must be a valid secret key\n\t\t\t\tkey:
string\n\t\t\t}\n\t\t}\n\t}]\n\n\t// +usage=Number of CPU units for
the service, like \n\tcpu?: string\n\n\t//
+usage=Specifies the attributes of the memory resource required for
the container.\n\tmemory?: string\n\n\tvolumeMounts?: {\n\t\t// +usage=Mount
PVC type volume\n\t\tpvc?: [...{\n\t\t\tname: string\n\t\t\tmountPath:
string\n\t\t\t// +usage=The name of the PVC\n\t\t\tclaimName: string\n\t\t}]\n\t\t//
+usage=Mount ConfigMap type volume\n\t\tconfigMap?: [...{\n\t\t\tname:
\ string\n\t\t\tmountPath: string\n\t\t\tdefaultMode: *420 |
int\n\t\t\tcmName: string\n\t\t\titems?: [...{\n\t\t\t\tkey: string\n\t\t\t\tpath:
string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount
Secret type volume\n\t\tsecret?: [...{\n\t\t\tname: string\n\t\t\tmountPath:
\ string\n\t\t\tdefaultMode: *420 | int\n\t\t\tsecretName: string\n\t\t\titems?:
[...{\n\t\t\t\tkey: string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
| int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount EmptyDir type volume\n\t\temptyDir?:
[...{\n\t\t\tname: string\n\t\t\tmountPath: string\n\t\t\tmedium:
\ *\"\" | \"Memory\"\n\t\t}]\n\t\t// +usage=Mount HostPath type volume\n\t\thostPath?:
[...{\n\t\t\tname: string\n\t\t\tmountPath: string\n\t\t\tpath:
\ string\n\t\t}]\n\t}\n\n\t// +usage=Deprecated field, use volumeMounts
instead.\n\tvolumes?: [...{\n\t\tname: string\n\t\tmountPath: string\n\t\t//
+usage=Specify volume type, options: \"pvc\",\"configMap\",\"secret\",\"emptyDir\"\n\t\ttype:
\"pvc\" | \"configMap\" | \"secret\" | \"emptyDir\"\n\t\tif type ==
\"pvc\" {\n\t\t\tclaimName: string\n\t\t}\n\t\tif type == \"configMap\"
{\n\t\t\tdefaultMode: *420 | int\n\t\t\tcmName: string\n\t\t\titems?:
[...{\n\t\t\t\tkey: string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
| int\n\t\t\t}]\n\t\t}\n\t\tif type == \"secret\" {\n\t\t\tdefaultMode:
*420 | int\n\t\t\tsecretName: string\n\t\t\titems?: [...{\n\t\t\t\tkey:
\ string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}\n\t\tif
type == \"emptyDir\" {\n\t\t\tmedium: *\"\" | \"Memory\"\n\t\t}\n\t}]\n\n\t//
+usage=Instructions for assessing whether the container is alive.\n\tlivenessProbe?:
#HealthProbe\n\n\t// +usage=Instructions for assessing whether the container
is in a suitable state to serve traffic.\n\treadinessProbe?: #HealthProbe\n\n\t//
+usage=Specify the hostAliases to add\n\thostAliases?: [...{\n\t\tip:
string\n\t\thostnames: [...string]\n\t}]\n}\n#HealthProbe: {\n\n\t//
+usage=Instructions for assessing container health by executing a command.
Either this attribute or the httpGet attribute or the tcpSocket attribute
MUST be specified. This attribute is mutually exclusive with both the
httpGet attribute and the tcpSocket attribute.\n\texec?: {\n\t\t// +usage=A
command to be executed inside the container to assess its health. Each
space delimited token of the command is a separate array element. Commands
exiting 0 are considered to be successful probes, whilst all other exit
codes are considered failures.\n\t\tcommand: [...string]\n\t}\n\n\t//
+usage=Instructions for assessing container health by executing an HTTP
GET request. Either this attribute or the exec attribute or the tcpSocket
attribute MUST be specified. This attribute is mutually exclusive with
both the exec attribute and the tcpSocket attribute.\n\thttpGet?: {\n\t\t//
+usage=The endpoint, relative to the port, to which the HTTP GET request
should be directed.\n\t\tpath: string\n\t\t// +usage=The TCP socket
within the container to which the HTTP GET request should be directed.\n\t\tport:
int\n\t\thttpHeaders?: [...{\n\t\t\tname: string\n\t\t\tvalue: string\n\t\t}]\n\t}\n\n\t//
+usage=Instructions for assessing container health by probing a TCP
socket. Either this attribute or the exec attribute or the httpGet attribute
MUST be specified. This attribute is mutually exclusive with both the
exec attribute and the httpGet attribute.\n\ttcpSocket?: {\n\t\t// +usage=The
TCP socket within the container that should be probed to assess container
health.\n\t\tport: int\n\t}\n\n\t// +usage=Number of seconds after the
container is started before the first probe is initiated.\n\tinitialDelaySeconds:
*0 | int\n\n\t// +usage=How often, in seconds, to execute the probe.\n\tperiodSeconds:
*10 | int\n\n\t// +usage=Number of seconds after which the probe times
out.\n\ttimeoutSeconds: *1 | int\n\n\t// +usage=Minimum consecutive
successes for the probe to be considered successful after having failed.\n\tsuccessThreshold:
*1 | int\n\n\t// +usage=Number of consecutive failures required to determine
the container is not alive (liveness probe) or not ready (readiness
probe).\n\tfailureThreshold: *3 | int\n}\n"
status:
customStatus: "ready: {\n\treadyReplicas: *0 | int\n} & {\n\tif context.output.status.readyReplicas
!= _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n}\nmessage:
\"Ready:\\(ready.readyReplicas)/\\(context.output.spec.replicas)\""
healthPolicy: "ready: {\n\tupdatedReplicas: *0 | int\n\treadyReplicas:
\ *0 | int\n\treplicas: *0 | int\n\tobservedGeneration:
*0 | int\n} & {\n\tif context.output.status.updatedReplicas != _|_ {\n\t\tupdatedReplicas:
context.output.status.updatedReplicas\n\t}\n\tif context.output.status.readyReplicas
!= _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n\tif
context.output.status.replicas != _|_ {\n\t\treplicas: context.output.status.replicas\n\t}\n\tif
context.output.status.observedGeneration != _|_ {\n\t\tobservedGeneration:
context.output.status.observedGeneration\n\t}\n}\nisHealth: (context.output.spec.replicas
== ready.readyReplicas) && (context.output.spec.replicas == ready.updatedReplicas)
&& (context.output.spec.replicas == ready.replicas) && (ready.observedGeneration
== context.output.metadata.generation || ready.observedGeneration > context.output.metadata.generation)"
workload:
definition:
apiVersion: apps/v1
kind: Deployment
type: deployments.apps
status: {}
traitDefinitions:
scaler:
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: Manually scale K8s pod for your workload
which follows the pod spec in path 'spec.template'.
meta.helm.sh/release-name: kubevela
meta.helm.sh/release-namespace: vela-system
labels:
app.kubernetes.io/managed-by: Helm
name: scaler
namespace: vela-system
spec:
appliesToWorkloads:
- '*'
definitionRef:
name: ""
schematic:
cue:
template: "parameter: {\n\t// +usage=Specify the number of workload\n\treplicas:
*1 | int\n}\n// +patchStrategy=retainKeys\npatch: spec: replicas: parameter.replicas\n"
status: {}
status: {}

View File

@@ -0,0 +1,271 @@
apiVersion: core.oam.dev/v1beta1
kind: ApplicationRevision
metadata:
annotations:
app.oam.dev/publishVersion: workflow-default-123456
name: backport-1-2-test-demo-v1
namespace: default
spec:
application:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
annotations:
app.oam.dev/publishVersion: workflow-default-123456
name: backport-1-2-test-demo
namespace: default
spec:
components:
- name: backport-1-2-test-demo
properties:
image: nginx
traits:
- properties:
replicas: 1
type: scaler
type: webservice
workflow:
steps:
- name: apply
type: apply-application-unknown
status: {}
componentDefinitions:
webservice:
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations:
definition.oam.dev/description: Describes long-running, scalable, containerized
services that have a stable network endpoint to receive external network
traffic from customers.
meta.helm.sh/release-name: kubevela
meta.helm.sh/release-namespace: vela-system
labels:
app.kubernetes.io/managed-by: Helm
name: webservice
namespace: vela-system
spec:
schematic:
cue:
template: "import (\n\t\"strconv\"\n)\n\nmountsArray: {\n\tpvc: *[\n\t\tfor
v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tname:
\ v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tconfigMap: *[\n\t\t\tfor
v in parameter.volumeMounts.configMap {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tsecret:
*[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\temptyDir:
*[\n\t\t\tfor v in parameter.volumeMounts.emptyDir {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\thostPath:
*[\n\t\t\tfor v in parameter.volumeMounts.hostPath {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n}\nvolumesArray:
{\n\tpvc: *[\n\t\tfor v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tname:
v.name\n\t\t\t\tpersistentVolumeClaim: claimName: v.claimName\n\t\t\t}\n\t\t},\n\t]
| []\n\n\tconfigMap: *[\n\t\t\tfor v in parameter.volumeMounts.configMap
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\tconfigMap: {\n\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\tname: v.cmName\n\t\t\t\t\tif v.items
!= _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
| []\n\n\tsecret: *[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tname:
v.name\n\t\t\t\tsecret: {\n\t\t\t\t\tdefaultMode: v.defaultMode\n\t\t\t\t\tsecretName:
\ v.secretName\n\t\t\t\t\tif v.items != _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
| []\n\n\temptyDir: *[\n\t\t\tfor v in parameter.volumeMounts.emptyDir
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\temptyDir: medium: v.medium\n\t\t\t}\n\t\t},\n\t]
| []\n\n\thostPath: *[\n\t\t\tfor v in parameter.volumeMounts.hostPath
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\thostPath: path: v.path\n\t\t\t}\n\t\t},\n\t]
| []\n}\noutput: {\n\tapiVersion: \"apps/v1\"\n\tkind: \"Deployment\"\n\tspec:
{\n\t\tselector: matchLabels: \"app.oam.dev/component\": context.name\n\n\t\ttemplate:
{\n\t\t\tmetadata: {\n\t\t\t\tlabels: {\n\t\t\t\t\tif parameter.labels
!= _|_ {\n\t\t\t\t\t\tparameter.labels\n\t\t\t\t\t}\n\t\t\t\t\tif parameter.addRevisionLabel
{\n\t\t\t\t\t\t\"app.oam.dev/revision\": context.revision\n\t\t\t\t\t}\n\t\t\t\t\t\"app.oam.dev/component\":
context.name\n\t\t\t\t}\n\t\t\t\tif parameter.annotations != _|_ {\n\t\t\t\t\tannotations:
parameter.annotations\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspec: {\n\t\t\t\tcontainers:
[{\n\t\t\t\t\tname: context.name\n\t\t\t\t\timage: parameter.image\n\t\t\t\t\tif
parameter[\"port\"] != _|_ && parameter[\"ports\"] == _|_ {\n\t\t\t\t\t\tports:
[{\n\t\t\t\t\t\t\tcontainerPort: parameter.port\n\t\t\t\t\t\t}]\n\t\t\t\t\t}\n\t\t\t\t\tif
parameter[\"ports\"] != _|_ {\n\t\t\t\t\t\tports: [ for v in parameter.ports
{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontainerPort: v.port\n\t\t\t\t\t\t\t\tprotocol:
\ v.protocol\n\t\t\t\t\t\t\t\tif v.name != _|_ {\n\t\t\t\t\t\t\t\t\tname:
v.name\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif v.name == _|_ {\n\t\t\t\t\t\t\t\t\tname:
\"port-\" + strconv.FormatInt(v.port, 10)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"imagePullPolicy\"] != _|_ {\n\t\t\t\t\t\timagePullPolicy:
parameter.imagePullPolicy\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"cmd\"]
!= _|_ {\n\t\t\t\t\t\tcommand: parameter.cmd\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"env\"] != _|_ {\n\t\t\t\t\t\tenv: parameter.env\n\t\t\t\t\t}\n\n\t\t\t\t\tif
context[\"config\"] != _|_ {\n\t\t\t\t\t\tenv: context.config\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"cpu\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
cpu: parameter.cpu\n\t\t\t\t\t\t\trequests: cpu: parameter.cpu\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"memory\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
memory: parameter.memory\n\t\t\t\t\t\t\trequests: memory: parameter.memory\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
{\n\t\t\t\t\t\tvolumeMounts: [ for v in parameter.volumes {\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmountPath:
v.mountPath\n\t\t\t\t\t\t\t\tname: v.name\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\t\tvolumeMounts: mountsArray.pvc
+ mountsArray.configMap + mountsArray.secret + mountsArray.emptyDir
+ mountsArray.hostPath\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"livenessProbe\"]
!= _|_ {\n\t\t\t\t\t\tlivenessProbe: parameter.livenessProbe\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"readinessProbe\"] != _|_ {\n\t\t\t\t\t\treadinessProbe:
parameter.readinessProbe\n\t\t\t\t\t}\n\n\t\t\t\t}]\n\n\t\t\t\tif parameter[\"hostAliases\"]
!= _|_ {\n\t\t\t\t\t// +patchKey=ip\n\t\t\t\t\thostAliases: parameter.hostAliases\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"imagePullSecrets\"] != _|_ {\n\t\t\t\t\timagePullSecrets:
[ for v in parameter.imagePullSecrets {\n\t\t\t\t\t\tname: v\n\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
{\n\t\t\t\t\tvolumes: [ for v in parameter.volumes {\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname:
v.name\n\t\t\t\t\t\t\tif v.type == \"pvc\" {\n\t\t\t\t\t\t\t\tpersistentVolumeClaim:
claimName: v.claimName\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif v.type ==
\"configMap\" {\n\t\t\t\t\t\t\t\tconfigMap: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\t\t\t\t\tname: v.cmName\n\t\t\t\t\t\t\t\t\tif
v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
v.type == \"secret\" {\n\t\t\t\t\t\t\t\tsecret: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\t\t\t\t\tsecretName: v.secretName\n\t\t\t\t\t\t\t\t\tif
v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
v.type == \"emptyDir\" {\n\t\t\t\t\t\t\t\temptyDir: medium: v.medium\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}]\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\tvolumes: volumesArray.pvc
+ volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir
+ volumesArray.hostPath\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nexposePorts:
[\n\tfor v in parameter.ports if v.expose == true {\n\t\tport: v.port\n\t\ttargetPort:
v.port\n\t\tif v.name != _|_ {\n\t\t\tname: v.name\n\t\t}\n\t\tif v.name
== _|_ {\n\t\t\tname: \"port-\" + strconv.FormatInt(v.port, 10)\n\t\t}\n\t},\n]\noutputs:
{\n\tif len(exposePorts) != 0 {\n\t\twebserviceExpose: {\n\t\t\tapiVersion:
\"v1\"\n\t\t\tkind: \"Service\"\n\t\t\tmetadata: name: context.name\n\t\t\tspec:
{\n\t\t\t\tselector: \"app.oam.dev/component\": context.name\n\t\t\t\tports:
exposePorts\n\t\t\t\ttype: parameter.exposeType\n\t\t\t}\n\t\t}\n\t}\n}\nparameter:
{\n\t// +usage=Specify the labels in the workload\n\tlabels?: [string]:
string\n\n\t// +usage=Specify the annotations in the workload\n\tannotations?:
[string]: string\n\n\t// +usage=Which image would you like to use for
your service\n\t// +short=i\n\timage: string\n\n\t// +usage=Specify
image pull policy for your service\n\timagePullPolicy?: \"Always\" |
\"Never\" | \"IfNotPresent\"\n\n\t// +usage=Specify image pull secrets
for your service\n\timagePullSecrets?: [...string]\n\n\t// +ignore\n\t//
+usage=Deprecated field, please use ports instead\n\t// +short=p\n\tport?:
int\n\n\t// +usage=Which ports do you want customer traffic sent to,
defaults to 80\n\tports?: [...{\n\t\t// +usage=Number of port to expose
on the pod's IP address\n\t\tport: int\n\t\t// +usage=Name of the port\n\t\tname?:
string\n\t\t// +usage=Protocol for port. Must be UDP, TCP, or SCTP\n\t\tprotocol:
*\"TCP\" | \"UDP\" | \"SCTP\"\n\t\t// +usage=Specify if the port should
be exposed\n\t\texpose: *false | bool\n\t}]\n\n\t// +ignore\n\t// +usage=Specify
what kind of Service you want. options: \"ClusterIP\", \"NodePort\",
\"LoadBalancer\", \"ExternalName\"\n\texposeType: *\"ClusterIP\" | \"NodePort\"
| \"LoadBalancer\" | \"ExternalName\"\n\n\t// +ignore\n\t// +usage=If
addRevisionLabel is true, the revision label will be added to the underlying
pods\n\taddRevisionLabel: *false | bool\n\n\t// +usage=Commands to run
in the container\n\tcmd?: [...string]\n\n\t// +usage=Define arguments
by using environment variables\n\tenv?: [...{\n\t\t// +usage=Environment
variable name\n\t\tname: string\n\t\t// +usage=The value of the environment
variable\n\t\tvalue?: string\n\t\t// +usage=Specifies a source the value
of this var should come from\n\t\tvalueFrom?: {\n\t\t\t// +usage=Selects
a key of a secret in the pod's namespace\n\t\t\tsecretKeyRef?: {\n\t\t\t\t//
+usage=The name of the secret in the pod's namespace to select from\n\t\t\t\tname:
string\n\t\t\t\t// +usage=The key of the secret to select from. Must
be a valid secret key\n\t\t\t\tkey: string\n\t\t\t}\n\t\t\t// +usage=Selects
a key of a config map in the pod's namespace\n\t\t\tconfigMapKeyRef?:
{\n\t\t\t\t// +usage=The name of the config map in the pod's namespace
to select from\n\t\t\t\tname: string\n\t\t\t\t// +usage=The key of the
config map to select from. Must be a valid secret key\n\t\t\t\tkey:
string\n\t\t\t}\n\t\t}\n\t}]\n\n\t// +usage=Number of CPU units for
the service, like \n\tcpu?: string\n\n\t//
+usage=Specifies the attributes of the memory resource required for
the container.\n\tmemory?: string\n\n\tvolumeMounts?: {\n\t\t// +usage=Mount
PVC type volume\n\t\tpvc?: [...{\n\t\t\tname: string\n\t\t\tmountPath:
string\n\t\t\t// +usage=The name of the PVC\n\t\t\tclaimName: string\n\t\t}]\n\t\t//
+usage=Mount ConfigMap type volume\n\t\tconfigMap?: [...{\n\t\t\tname:
\ string\n\t\t\tmountPath: string\n\t\t\tdefaultMode: *420 |
int\n\t\t\tcmName: string\n\t\t\titems?: [...{\n\t\t\t\tkey: string\n\t\t\t\tpath:
string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount
Secret type volume\n\t\tsecret?: [...{\n\t\t\tname: string\n\t\t\tmountPath:
\ string\n\t\t\tdefaultMode: *420 | int\n\t\t\tsecretName: string\n\t\t\titems?:
[...{\n\t\t\t\tkey: string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
| int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount EmptyDir type volume\n\t\temptyDir?:
[...{\n\t\t\tname: string\n\t\t\tmountPath: string\n\t\t\tmedium:
\ *\"\" | \"Memory\"\n\t\t}]\n\t\t// +usage=Mount HostPath type volume\n\t\thostPath?:
[...{\n\t\t\tname: string\n\t\t\tmountPath: string\n\t\t\tpath:
\ string\n\t\t}]\n\t}\n\n\t// +usage=Deprecated field, use volumeMounts
instead.\n\tvolumes?: [...{\n\t\tname: string\n\t\tmountPath: string\n\t\t//
+usage=Specify volume type, options: \"pvc\",\"configMap\",\"secret\",\"emptyDir\"\n\t\ttype:
\"pvc\" | \"configMap\" | \"secret\" | \"emptyDir\"\n\t\tif type ==
\"pvc\" {\n\t\t\tclaimName: string\n\t\t}\n\t\tif type == \"configMap\"
{\n\t\t\tdefaultMode: *420 | int\n\t\t\tcmName: string\n\t\t\titems?:
[...{\n\t\t\t\tkey: string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
| int\n\t\t\t}]\n\t\t}\n\t\tif type == \"secret\" {\n\t\t\tdefaultMode:
*420 | int\n\t\t\tsecretName: string\n\t\t\titems?: [...{\n\t\t\t\tkey:
\ string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}\n\t\tif
type == \"emptyDir\" {\n\t\t\tmedium: *\"\" | \"Memory\"\n\t\t}\n\t}]\n\n\t//
+usage=Instructions for assessing whether the container is alive.\n\tlivenessProbe?:
#HealthProbe\n\n\t// +usage=Instructions for assessing whether the container
is in a suitable state to serve traffic.\n\treadinessProbe?: #HealthProbe\n\n\t//
+usage=Specify the hostAliases to add\n\thostAliases?: [...{\n\t\tip:
string\n\t\thostnames: [...string]\n\t}]\n}\n#HealthProbe: {\n\n\t//
+usage=Instructions for assessing container health by executing a command.
Either this attribute or the httpGet attribute or the tcpSocket attribute
MUST be specified. This attribute is mutually exclusive with both the
httpGet attribute and the tcpSocket attribute.\n\texec?: {\n\t\t// +usage=A
command to be executed inside the container to assess its health. Each
space delimited token of the command is a separate array element. Commands
exiting 0 are considered to be successful probes, whilst all other exit
codes are considered failures.\n\t\tcommand: [...string]\n\t}\n\n\t//
+usage=Instructions for assessing container health by executing an HTTP
GET request. Either this attribute or the exec attribute or the tcpSocket
attribute MUST be specified. This attribute is mutually exclusive with
both the exec attribute and the tcpSocket attribute.\n\thttpGet?: {\n\t\t//
+usage=The endpoint, relative to the port, to which the HTTP GET request
should be directed.\n\t\tpath: string\n\t\t// +usage=The TCP socket
within the container to which the HTTP GET request should be directed.\n\t\tport:
int\n\t\thttpHeaders?: [...{\n\t\t\tname: string\n\t\t\tvalue: string\n\t\t}]\n\t}\n\n\t//
+usage=Instructions for assessing container health by probing a TCP
socket. Either this attribute or the exec attribute or the httpGet attribute
MUST be specified. This attribute is mutually exclusive with both the
exec attribute and the httpGet attribute.\n\ttcpSocket?: {\n\t\t// +usage=The
TCP socket within the container that should be probed to assess container
health.\n\t\tport: int\n\t}\n\n\t// +usage=Number of seconds after the
container is started before the first probe is initiated.\n\tinitialDelaySeconds:
*0 | int\n\n\t// +usage=How often, in seconds, to execute the probe.\n\tperiodSeconds:
*10 | int\n\n\t// +usage=Number of seconds after which the probe times
out.\n\ttimeoutSeconds: *1 | int\n\n\t// +usage=Minimum consecutive
successes for the probe to be considered successful after having failed.\n\tsuccessThreshold:
*1 | int\n\n\t// +usage=Number of consecutive failures required to determine
the container is not alive (liveness probe) or not ready (readiness
probe).\n\tfailureThreshold: *3 | int\n}\n"
status:
customStatus: "ready: {\n\treadyReplicas: *0 | int\n} & {\n\tif context.output.status.readyReplicas
!= _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n}\nmessage:
\"Ready:\\(ready.readyReplicas)/\\(context.output.spec.replicas)\""
healthPolicy: "ready: {\n\tupdatedReplicas: *0 | int\n\treadyReplicas:
\ *0 | int\n\treplicas: *0 | int\n\tobservedGeneration:
*0 | int\n} & {\n\tif context.output.status.updatedReplicas != _|_ {\n\t\tupdatedReplicas:
context.output.status.updatedReplicas\n\t}\n\tif context.output.status.readyReplicas
!= _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n\tif
context.output.status.replicas != _|_ {\n\t\treplicas: context.output.status.replicas\n\t}\n\tif
context.output.status.observedGeneration != _|_ {\n\t\tobservedGeneration:
context.output.status.observedGeneration\n\t}\n}\nisHealth: (context.output.spec.replicas
== ready.readyReplicas) && (context.output.spec.replicas == ready.updatedReplicas)
&& (context.output.spec.replicas == ready.replicas) && (ready.observedGeneration
== context.output.metadata.generation || ready.observedGeneration > context.output.metadata.generation)"
workload:
definition:
apiVersion: apps/v1
kind: Deployment
type: deployments.apps
status: {}
traitDefinitions:
scaler:
apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: Manually scale K8s pod for your workload
which follows the pod spec in path 'spec.template'.
meta.helm.sh/release-name: kubevela
meta.helm.sh/release-namespace: vela-system
labels:
app.kubernetes.io/managed-by: Helm
name: scaler
namespace: vela-system
spec:
appliesToWorkloads:
- '*'
definitionRef:
name: ""
schematic:
cue:
template: "parameter: {\n\t// +usage=Specify the number of workload\n\treplicas:
*1 | int\n}\n// +patchStrategy=retainKeys\npatch: spec: replicas: parameter.replicas\n"
status: {}
status: {}

View File

@@ -0,0 +1,19 @@
apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Apply application for your workflow steps
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application
namespace: vela-system
spec:
schematic:
cue:
template: |
import (
"vela/op"
)
// apply application
output: op.#ApplyApplication & {}

View File

@@ -26,7 +26,7 @@ import (
openapi "github.com/alibabacloud-go/darabonba-openapi/client"
"github.com/alibabacloud-go/tea/tea"
types "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime"
v1beta12 "github.com/oam-dev/terraform-controller/api/v1beta1"
v1beta12 "github.com/oam-dev/terraform-controller/api/v1beta2"
"github.com/pkg/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"

View File

@@ -18,8 +18,9 @@ package cmd
import (
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/term"
cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
)
// Builder build command with factory

View File

@@ -17,8 +17,9 @@ limitations under the License.
package cmd
import (
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/controller-runtime/pkg/client"
cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
)
// Factory client factory for running command

32
pkg/cmd/util/helpers.go Normal file
View File

@@ -0,0 +1,32 @@
/*
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 util
import (
"github.com/pkg/errors"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
)
// CheckErr wraps the kubectl CheckErr func by preventing inappropriate type conversion and panic
func CheckErr(err error) {
defer func() {
if r := recover(); r != nil {
cmdutil.CheckErr(errors.New(err.Error()))
}
}()
cmdutil.CheckErr(err)
}

View File

@@ -18,9 +18,9 @@ package cmd
import (
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"github.com/oam-dev/kubevela/apis/types"
cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/env"
)

View File

@@ -21,9 +21,11 @@ import (
"sync"
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
terraforv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
terraforv1beta2 "github.com/oam-dev/terraform-controller/api/v1beta2"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -32,11 +34,10 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/appfile"
"github.com/oam-dev/kubevela/pkg/oam"
monitorContext "github.com/oam-dev/kubevela/pkg/monitor/context"
"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/resourcekeeper"
)
@@ -229,34 +230,22 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
)
if wl.CapabilityCategory == types.TerraformCategory {
var configuration terraformapi.Configuration
var configuration terraforv1beta2.Configuration
if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: namespace}, &configuration); err != nil {
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, check health error", appName, wl.Name)
}
isLatest := func() bool {
if configuration.Status.ObservedGeneration != 0 {
if configuration.Status.ObservedGeneration != configuration.Generation {
return false
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 {
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, check health error", appName, wl.Name)
}
isHealth = setStatus(&status, legacyConfiguration.Status.ObservedGeneration, legacyConfiguration.Generation,
legacyConfiguration.GetLabels(), appRev.Name, legacyConfiguration.Status.Apply.State, legacyConfiguration.Status.Apply.Message)
} else {
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, check health error", appName, wl.Name)
}
// Use AppRevision to avoid getting the configuration before the patch.
if v, ok := configuration.GetLabels()[oam.LabelAppRevision]; ok {
if v != appRev.Name {
return false
}
}
return true
}
if !isLatest() || configuration.Status.Apply.State != terraformtypes.Available {
status.Healthy = false
isHealth = false
} else {
status.Healthy = true
isHealth = true
isHealth = setStatus(&status, configuration.Status.ObservedGeneration, configuration.Generation, configuration.GetLabels(),
appRev.Name, configuration.Status.Apply.State, configuration.Status.Apply.Message)
}
status.Message = configuration.Status.Apply.Message
} else {
if ok, err := wl.EvalHealth(wl.Ctx, h.r.Client, namespace); !ok || err != nil {
isHealth = false
@@ -292,6 +281,29 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
return &status, isHealth, nil
}
func setStatus(status *common.ApplicationComponentStatus, observedGeneration, generation int64, labels map[string]string,
appRevName string, state terraformtypes.ConfigurationState, message string) bool {
isLatest := func() bool {
if observedGeneration != 0 && observedGeneration != generation {
return false
}
// Use AppRevision to avoid getting the configuration before the patch.
if v, ok := labels[oam.LabelAppRevision]; ok {
if v != appRevName {
return false
}
}
return true
}
if !isLatest() || state != terraformtypes.Available {
status.Healthy = false
return false
}
status.Healthy = true
status.Message = message
return true
}
func generateScopeReference(scopes []appfile.Scope) []corev1.ObjectReference {
var references []corev1.ObjectReference
for _, scope := range scopes {

View File

@@ -26,7 +26,7 @@ import (
"github.com/oam-dev/kubevela/pkg/oam/testutil"
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
appsv1 "k8s.io/api/apps/v1"
@@ -343,7 +343,7 @@ var _ = Describe("Test Application health check", func() {
Spec: v1beta1.ComponentDefinitionSpec{
Workload: common.WorkloadTypeDescriptor{
Definition: common.WorkloadGVK{
APIVersion: "terraform.core.oam.dev/v1beta1",
APIVersion: "terraform.core.oam.dev/v1beta2",
Kind: "Configuration",
},
},

View File

@@ -410,10 +410,12 @@ func (h *AppHandler) currentAppRevIsNew(ctx context.Context) (bool, bool, error)
if isLatestRev {
appSpec := h.currentAppRev.Spec.Application.Spec
traitDef := h.currentAppRev.Spec.TraitDefinitions
workflowStepDef := h.currentAppRev.Spec.WorkflowStepDefinitions
h.currentAppRev = h.latestAppRev.DeepCopy()
h.currentRevHash = h.app.Status.LatestRevision.RevisionHash
h.currentAppRev.Spec.Application.Spec = appSpec
h.currentAppRev.Spec.TraitDefinitions = traitDef
h.currentAppRev.Spec.WorkflowStepDefinitions = workflowStepDef
return false, false, nil
}
@@ -755,6 +757,7 @@ func (h *AppHandler) FinalizeAndApplyAppRevision(ctx context.Context) error {
appRev.SetGroupVersionKind(v1beta1.ApplicationRevisionGroupVersionKind)
// pass application's annotations & labels to app revision
appRev.SetAnnotations(h.app.GetAnnotations())
delete(appRev.Annotations, oam.AnnotationLastAppliedConfiguration)
appRev.SetLabels(h.app.GetLabels())
util.AddLabels(appRev, map[string]string{
oam.LabelAppName: h.app.GetName(),

View File

@@ -19,6 +19,8 @@ package application
import (
"context"
"encoding/json"
"fmt"
"reflect"
"strconv"
"time"
@@ -825,3 +827,347 @@ var _ = Describe("Test remove SkipAppRev func", func() {
Expect(res.Components[0].Traits[1].Type).Should(BeEquivalentTo("service"))
})
})
var _ = Describe("Test PrepareCurrentAppRevision", func() {
var app v1beta1.Application
var apprev v1beta1.ApplicationRevision
ctx := context.Background()
var handler *AppHandler
BeforeEach(func() {
// prepare ComponentDefinition
var compd v1beta1.ComponentDefinition
Expect(yaml.Unmarshal([]byte(componentDefYaml), &compd)).To(Succeed())
Expect(k8sClient.Create(ctx, &compd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
// prepare WorkflowStepDefinition
wsdYaml := `
apiVersion: core.oam.dev/v1beta1
kind: WorkflowStepDefinition
metadata:
annotations:
definition.oam.dev/description: Apply application for your workflow steps
labels:
custom.definition.oam.dev/ui-hidden: "true"
name: apply-application
namespace: vela-system
spec:
schematic:
cue:
template: |
import (
"vela/op"
)
// apply application
output: op.#ApplyApplication & {}
`
var wsd v1beta1.WorkflowStepDefinition
Expect(yaml.Unmarshal([]byte(wsdYaml), &wsd)).To(Succeed())
Expect(k8sClient.Create(ctx, &wsd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
// prepare application and application revision
appYaml := `
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: backport-1-2-test-demo
namespace: default
spec:
components:
- name: backport-1-2-test-demo
properties:
image: nginx
type: worker
workflow:
steps:
- name: apply
type: apply-application
status:
latestRevision:
name: backport-1-2-test-demo-v1
revision: 1
revisionHash: 38ddf4e721073703
`
Expect(yaml.Unmarshal([]byte(appYaml), &app)).To(Succeed())
Expect(k8sClient.Create(ctx, &app)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
// prepare application revision
apprevYaml := `
apiVersion: core.oam.dev/v1beta1
kind: ApplicationRevision
metadata:
name: backport-1-2-test-demo-v1
namespace: default
ownerReferences:
- apiVersion: core.oam.dev/v1beta1
controller: true
kind: Application
name: backport-1-2-test-demo
uid: b69fab34-7058-412b-994d-1465a9421f06
spec:
application:
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: backport-1-2-test-demo
namespace: default
spec:
components:
- name: backport-1-2-test-demo
properties:
image: nginx
type: worker
status: {}
componentDefinitions:
webservice:
apiVersion: core.oam.dev/v1beta1
kind: ComponentDefinition
metadata:
annotations:
definition.oam.dev/description: Describes long-running, scalable, containerized
services that have a stable network endpoint to receive external network
traffic from customers.
meta.helm.sh/release-name: kubevela
meta.helm.sh/release-namespace: vela-system
labels:
app.kubernetes.io/managed-by: Helm
name: webservice
namespace: vela-system
spec:
schematic:
cue:
template: "import (\n\t\"strconv\"\n)\n\nmountsArray: {\n\tpvc: *[\n\t\tfor
v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tmountPath: v.mountPath\n\t\t\t\tname:
\ v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tconfigMap: *[\n\t\t\tfor
v in parameter.volumeMounts.configMap {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\tsecret:
*[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\temptyDir:
*[\n\t\t\tfor v in parameter.volumeMounts.emptyDir {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n\n\thostPath:
*[\n\t\t\tfor v in parameter.volumeMounts.hostPath {\n\t\t\t{\n\t\t\t\tmountPath:
v.mountPath\n\t\t\t\tname: v.name\n\t\t\t}\n\t\t},\n\t] | []\n}\nvolumesArray:
{\n\tpvc: *[\n\t\tfor v in parameter.volumeMounts.pvc {\n\t\t\t{\n\t\t\t\tname:
v.name\n\t\t\t\tpersistentVolumeClaim: claimName: v.claimName\n\t\t\t}\n\t\t},\n\t]
| []\n\n\tconfigMap: *[\n\t\t\tfor v in parameter.volumeMounts.configMap
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\tconfigMap: {\n\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\tname: v.cmName\n\t\t\t\t\tif v.items
!= _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
| []\n\n\tsecret: *[\n\t\tfor v in parameter.volumeMounts.secret {\n\t\t\t{\n\t\t\t\tname:
v.name\n\t\t\t\tsecret: {\n\t\t\t\t\tdefaultMode: v.defaultMode\n\t\t\t\t\tsecretName:
\ v.secretName\n\t\t\t\t\tif v.items != _|_ {\n\t\t\t\t\t\titems: v.items\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t]
| []\n\n\temptyDir: *[\n\t\t\tfor v in parameter.volumeMounts.emptyDir
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\temptyDir: medium: v.medium\n\t\t\t}\n\t\t},\n\t]
| []\n\n\thostPath: *[\n\t\t\tfor v in parameter.volumeMounts.hostPath
{\n\t\t\t{\n\t\t\t\tname: v.name\n\t\t\t\thostPath: path: v.path\n\t\t\t}\n\t\t},\n\t]
| []\n}\noutput: {\n\tapiVersion: \"apps/v1\"\n\tkind: \"Deployment\"\n\tspec:
{\n\t\tselector: matchLabels: \"app.oam.dev/component\": context.name\n\n\t\ttemplate:
{\n\t\t\tmetadata: {\n\t\t\t\tlabels: {\n\t\t\t\t\tif parameter.labels
!= _|_ {\n\t\t\t\t\t\tparameter.labels\n\t\t\t\t\t}\n\t\t\t\t\tif parameter.addRevisionLabel
{\n\t\t\t\t\t\t\"app.oam.dev/revision\": context.revision\n\t\t\t\t\t}\n\t\t\t\t\t\"app.oam.dev/component\":
context.name\n\t\t\t\t}\n\t\t\t\tif parameter.annotations != _|_ {\n\t\t\t\t\tannotations:
parameter.annotations\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tspec: {\n\t\t\t\tcontainers:
[{\n\t\t\t\t\tname: context.name\n\t\t\t\t\timage: parameter.image\n\t\t\t\t\tif
parameter[\"port\"] != _|_ && parameter[\"ports\"] == _|_ {\n\t\t\t\t\t\tports:
[{\n\t\t\t\t\t\t\tcontainerPort: parameter.port\n\t\t\t\t\t\t}]\n\t\t\t\t\t}\n\t\t\t\t\tif
parameter[\"ports\"] != _|_ {\n\t\t\t\t\t\tports: [ for v in parameter.ports
{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tcontainerPort: v.port\n\t\t\t\t\t\t\t\tprotocol:
\ v.protocol\n\t\t\t\t\t\t\t\tif v.name != _|_ {\n\t\t\t\t\t\t\t\t\tname:
v.name\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif v.name == _|_ {\n\t\t\t\t\t\t\t\t\tname:
\"port-\" + strconv.FormatInt(v.port, 10)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"imagePullPolicy\"] != _|_ {\n\t\t\t\t\t\timagePullPolicy:
parameter.imagePullPolicy\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"cmd\"]
!= _|_ {\n\t\t\t\t\t\tcommand: parameter.cmd\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"env\"] != _|_ {\n\t\t\t\t\t\tenv: parameter.env\n\t\t\t\t\t}\n\n\t\t\t\t\tif
context[\"config\"] != _|_ {\n\t\t\t\t\t\tenv: context.config\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"cpu\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
cpu: parameter.cpu\n\t\t\t\t\t\t\trequests: cpu: parameter.cpu\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"memory\"] != _|_ {\n\t\t\t\t\t\tresources: {\n\t\t\t\t\t\t\tlimits:
memory: parameter.memory\n\t\t\t\t\t\t\trequests: memory: parameter.memory\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
{\n\t\t\t\t\t\tvolumeMounts: [ for v in parameter.volumes {\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmountPath:
v.mountPath\n\t\t\t\t\t\t\t\tname: v.name\n\t\t\t\t\t\t\t}}]\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\t\tvolumeMounts: mountsArray.pvc
+ mountsArray.configMap + mountsArray.secret + mountsArray.emptyDir
+ mountsArray.hostPath\n\t\t\t\t\t}\n\n\t\t\t\t\tif parameter[\"livenessProbe\"]
!= _|_ {\n\t\t\t\t\t\tlivenessProbe: parameter.livenessProbe\n\t\t\t\t\t}\n\n\t\t\t\t\tif
parameter[\"readinessProbe\"] != _|_ {\n\t\t\t\t\t\treadinessProbe:
parameter.readinessProbe\n\t\t\t\t\t}\n\n\t\t\t\t}]\n\n\t\t\t\tif parameter[\"hostAliases\"]
!= _|_ {\n\t\t\t\t\t// +patchKey=ip\n\t\t\t\t\thostAliases: parameter.hostAliases\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"imagePullSecrets\"] != _|_ {\n\t\t\t\t\timagePullSecrets:
[ for v in parameter.imagePullSecrets {\n\t\t\t\t\t\tname: v\n\t\t\t\t\t},\n\t\t\t\t\t]\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"volumes\"] != _|_ && parameter[\"volumeMounts\"] == _|_
{\n\t\t\t\t\tvolumes: [ for v in parameter.volumes {\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tname:
v.name\n\t\t\t\t\t\t\tif v.type == \"pvc\" {\n\t\t\t\t\t\t\t\tpersistentVolumeClaim:
claimName: v.claimName\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif v.type ==
\"configMap\" {\n\t\t\t\t\t\t\t\tconfigMap: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\t\t\t\t\tname: v.cmName\n\t\t\t\t\t\t\t\t\tif
v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
v.type == \"secret\" {\n\t\t\t\t\t\t\t\tsecret: {\n\t\t\t\t\t\t\t\t\tdefaultMode:
v.defaultMode\n\t\t\t\t\t\t\t\t\tsecretName: v.secretName\n\t\t\t\t\t\t\t\t\tif
v.items != _|_ {\n\t\t\t\t\t\t\t\t\t\titems: v.items\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif
v.type == \"emptyDir\" {\n\t\t\t\t\t\t\t\temptyDir: medium: v.medium\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}]\n\t\t\t\t}\n\n\t\t\t\tif
parameter[\"volumeMounts\"] != _|_ {\n\t\t\t\t\tvolumes: volumesArray.pvc
+ volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir
+ volumesArray.hostPath\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\nexposePorts:
[\n\tfor v in parameter.ports if v.expose == true {\n\t\tport: v.port\n\t\ttargetPort:
v.port\n\t\tif v.name != _|_ {\n\t\t\tname: v.name\n\t\t}\n\t\tif v.name
== _|_ {\n\t\t\tname: \"port-\" + strconv.FormatInt(v.port, 10)\n\t\t}\n\t},\n]\noutputs:
{\n\tif len(exposePorts) != 0 {\n\t\twebserviceExpose: {\n\t\t\tapiVersion:
\"v1\"\n\t\t\tkind: \"Service\"\n\t\t\tmetadata: name: context.name\n\t\t\tspec:
{\n\t\t\t\tselector: \"app.oam.dev/component\": context.name\n\t\t\t\tports:
exposePorts\n\t\t\t\ttype: parameter.exposeType\n\t\t\t}\n\t\t}\n\t}\n}\nparameter:
{\n\t// +usage=Specify the labels in the workload\n\tlabels?: [string]:
string\n\n\t// +usage=Specify the annotations in the workload\n\tannotations?:
[string]: string\n\n\t// +usage=Which image would you like to use for
your service\n\t// +short=i\n\timage: string\n\n\t// +usage=Specify
image pull policy for your service\n\timagePullPolicy?: \"Always\" |
\"Never\" | \"IfNotPresent\"\n\n\t// +usage=Specify image pull secrets
for your service\n\timagePullSecrets?: [...string]\n\n\t// +ignore\n\t//
+usage=Deprecated field, please use ports instead\n\t// +short=p\n\tport?:
int\n\n\t// +usage=Which ports do you want customer traffic sent to,
defaults to 80\n\tports?: [...{\n\t\t// +usage=Number of port to expose
on the pod's IP address\n\t\tport: int\n\t\t// +usage=Name of the port\n\t\tname?:
string\n\t\t// +usage=Protocol for port. Must be UDP, TCP, or SCTP\n\t\tprotocol:
*\"TCP\" | \"UDP\" | \"SCTP\"\n\t\t// +usage=Specify if the port should
be exposed\n\t\texpose: *false | bool\n\t}]\n\n\t// +ignore\n\t// +usage=Specify
what kind of Service you want. options: \"ClusterIP\", \"NodePort\",
\"LoadBalancer\", \"ExternalName\"\n\texposeType: *\"ClusterIP\" | \"NodePort\"
| \"LoadBalancer\" | \"ExternalName\"\n\n\t// +ignore\n\t// +usage=If
addRevisionLabel is true, the revision label will be added to the underlying
pods\n\taddRevisionLabel: *false | bool\n\n\t// +usage=Commands to run
in the container\n\tcmd?: [...string]\n\n\t// +usage=Define arguments
by using environment variables\n\tenv?: [...{\n\t\t// +usage=Environment
variable name\n\t\tname: string\n\t\t// +usage=The value of the environment
variable\n\t\tvalue?: string\n\t\t// +usage=Specifies a source the value
of this var should come from\n\t\tvalueFrom?: {\n\t\t\t// +usage=Selects
a key of a secret in the pod's namespace\n\t\t\tsecretKeyRef?: {\n\t\t\t\t//
+usage=The name of the secret in the pod's namespace to select from\n\t\t\t\tname:
string\n\t\t\t\t// +usage=The key of the secret to select from. Must
be a valid secret key\n\t\t\t\tkey: string\n\t\t\t}\n\t\t\t// +usage=Selects
a key of a config map in the pod's namespace\n\t\t\tconfigMapKeyRef?:
{\n\t\t\t\t// +usage=The name of the config map in the pod's namespace
to select from\n\t\t\t\tname: string\n\t\t\t\t// +usage=The key of the
config map to select from. Must be a valid secret key\n\t\t\t\tkey:
string\n\t\t\t}\n\t\t}\n\t}]\n\n\t// +usage=Number of CPU units for
the service, like \n\tcpu?: string\n\n\t//
+usage=Specifies the attributes of the memory resource required for
the container.\n\tmemory?: string\n\n\tvolumeMounts?: {\n\t\t// +usage=Mount
PVC type volume\n\t\tpvc?: [...{\n\t\t\tname: string\n\t\t\tmountPath:
string\n\t\t\t// +usage=The name of the PVC\n\t\t\tclaimName: string\n\t\t}]\n\t\t//
+usage=Mount ConfigMap type volume\n\t\tconfigMap?: [...{\n\t\t\tname:
\ string\n\t\t\tmountPath: string\n\t\t\tdefaultMode: *420 |
int\n\t\t\tcmName: string\n\t\t\titems?: [...{\n\t\t\t\tkey: string\n\t\t\t\tpath:
string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount
Secret type volume\n\t\tsecret?: [...{\n\t\t\tname: string\n\t\t\tmountPath:
\ string\n\t\t\tdefaultMode: *420 | int\n\t\t\tsecretName: string\n\t\t\titems?:
[...{\n\t\t\t\tkey: string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
| int\n\t\t\t}]\n\t\t}]\n\t\t// +usage=Mount EmptyDir type volume\n\t\temptyDir?:
[...{\n\t\t\tname: string\n\t\t\tmountPath: string\n\t\t\tmedium:
\ *\"\" | \"Memory\"\n\t\t}]\n\t\t// +usage=Mount HostPath type volume\n\t\thostPath?:
[...{\n\t\t\tname: string\n\t\t\tmountPath: string\n\t\t\tpath:
\ string\n\t\t}]\n\t}\n\n\t// +usage=Deprecated field, use volumeMounts
instead.\n\tvolumes?: [...{\n\t\tname: string\n\t\tmountPath: string\n\t\t//
+usage=Specify volume type, options: \"pvc\",\"configMap\",\"secret\",\"emptyDir\"\n\t\ttype:
\"pvc\" | \"configMap\" | \"secret\" | \"emptyDir\"\n\t\tif type ==
\"pvc\" {\n\t\t\tclaimName: string\n\t\t}\n\t\tif type == \"configMap\"
{\n\t\t\tdefaultMode: *420 | int\n\t\t\tcmName: string\n\t\t\titems?:
[...{\n\t\t\t\tkey: string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511
| int\n\t\t\t}]\n\t\t}\n\t\tif type == \"secret\" {\n\t\t\tdefaultMode:
*420 | int\n\t\t\tsecretName: string\n\t\t\titems?: [...{\n\t\t\t\tkey:
\ string\n\t\t\t\tpath: string\n\t\t\t\tmode: *511 | int\n\t\t\t}]\n\t\t}\n\t\tif
type == \"emptyDir\" {\n\t\t\tmedium: *\"\" | \"Memory\"\n\t\t}\n\t}]\n\n\t//
+usage=Instructions for assessing whether the container is alive.\n\tlivenessProbe?:
#HealthProbe\n\n\t// +usage=Instructions for assessing whether the container
is in a suitable state to serve traffic.\n\treadinessProbe?: #HealthProbe\n\n\t//
+usage=Specify the hostAliases to add\n\thostAliases?: [...{\n\t\tip:
string\n\t\thostnames: [...string]\n\t}]\n}\n#HealthProbe: {\n\n\t//
+usage=Instructions for assessing container health by executing a command.
Either this attribute or the httpGet attribute or the tcpSocket attribute
MUST be specified. This attribute is mutually exclusive with both the
httpGet attribute and the tcpSocket attribute.\n\texec?: {\n\t\t// +usage=A
command to be executed inside the container to assess its health. Each
space delimited token of the command is a separate array element. Commands
exiting 0 are considered to be successful probes, whilst all other exit
codes are considered failures.\n\t\tcommand: [...string]\n\t}\n\n\t//
+usage=Instructions for assessing container health by executing an HTTP
GET request. Either this attribute or the exec attribute or the tcpSocket
attribute MUST be specified. This attribute is mutually exclusive with
both the exec attribute and the tcpSocket attribute.\n\thttpGet?: {\n\t\t//
+usage=The endpoint, relative to the port, to which the HTTP GET request
should be directed.\n\t\tpath: string\n\t\t// +usage=The TCP socket
within the container to which the HTTP GET request should be directed.\n\t\tport:
int\n\t\thttpHeaders?: [...{\n\t\t\tname: string\n\t\t\tvalue: string\n\t\t}]\n\t}\n\n\t//
+usage=Instructions for assessing container health by probing a TCP
socket. Either this attribute or the exec attribute or the httpGet attribute
MUST be specified. This attribute is mutually exclusive with both the
exec attribute and the httpGet attribute.\n\ttcpSocket?: {\n\t\t// +usage=The
TCP socket within the container that should be probed to assess container
health.\n\t\tport: int\n\t}\n\n\t// +usage=Number of seconds after the
container is started before the first probe is initiated.\n\tinitialDelaySeconds:
*0 | int\n\n\t// +usage=How often, in seconds, to execute the probe.\n\tperiodSeconds:
*10 | int\n\n\t// +usage=Number of seconds after which the probe times
out.\n\ttimeoutSeconds: *1 | int\n\n\t// +usage=Minimum consecutive
successes for the probe to be considered successful after having failed.\n\tsuccessThreshold:
*1 | int\n\n\t// +usage=Number of consecutive failures required to determine
the container is not alive (liveness probe) or not ready (readiness
probe).\n\tfailureThreshold: *3 | int\n}\n"
status:
customStatus: "ready: {\n\treadyReplicas: *0 | int\n} & {\n\tif context.output.status.readyReplicas
!= _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n}\nmessage:
\"Ready:\\(ready.readyReplicas)/\\(context.output.spec.replicas)\""
healthPolicy: "ready: {\n\tupdatedReplicas: *0 | int\n\treadyReplicas:
\ *0 | int\n\treplicas: *0 | int\n\tobservedGeneration:
*0 | int\n} & {\n\tif context.output.status.updatedReplicas != _|_ {\n\t\tupdatedReplicas:
context.output.status.updatedReplicas\n\t}\n\tif context.output.status.readyReplicas
!= _|_ {\n\t\treadyReplicas: context.output.status.readyReplicas\n\t}\n\tif
context.output.status.replicas != _|_ {\n\t\treplicas: context.output.status.replicas\n\t}\n\tif
context.output.status.observedGeneration != _|_ {\n\t\tobservedGeneration:
context.output.status.observedGeneration\n\t}\n}\nisHealth: (context.output.spec.replicas
== ready.readyReplicas) && (context.output.spec.replicas == ready.updatedReplicas)
&& (context.output.spec.replicas == ready.replicas) && (ready.observedGeneration
== context.output.metadata.generation || ready.observedGeneration > context.output.metadata.generation)"
workload:
definition:
apiVersion: apps/v1
kind: Deployment
type: deployments.apps
status: {}
status: {}
`
Expect(yaml.Unmarshal([]byte(apprevYaml), &apprev)).To(Succeed())
// simulate 1.2 version that WorkflowStepDefinitions are not patched in appliacation revision
apprev.ObjectMeta.OwnerReferences[0].UID = app.ObjectMeta.UID
Expect(k8sClient.Create(ctx, &apprev)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
// prepare handler
_handler, err := NewAppHandler(ctx, reconciler, &app, nil)
Expect(err).Should(Succeed())
handler = _handler
})
It("Test currentAppRevIsNew func", func() {
By("Backport 1.2 version that WorkflowStepDefinitions are not patched to application revision")
// generate appfile
appfile, err := appfile.NewApplicationParser(reconciler.Client, reconciler.dm, reconciler.pd).GenerateAppFile(ctx, &app)
ctx = util.SetNamespaceInCtx(ctx, app.Namespace)
Expect(err).To(Succeed())
Expect(handler.PrepareCurrentAppRevision(ctx, appfile)).Should(Succeed())
// prepare apprev
thisWSD := handler.currentAppRev.Spec.WorkflowStepDefinitions
Expect(len(thisWSD) > 0 && func() bool {
expected := appfile.RelatedWorkflowStepDefinitions
for i, w := range thisWSD {
expW := *(expected[i])
if !reflect.DeepEqual(w, expW) {
fmt.Printf("appfile wsd:%s apprev wsd%s", w.Name, expW.Name)
return false
}
}
return true
}()).Should(BeTrue())
})
})

View File

@@ -28,7 +28,7 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/event"
"github.com/go-logr/logr"
terraformv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
terraformv1beta2 "github.com/oam-dev/terraform-controller/api/v1beta2"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/pkg/errors"
@@ -121,7 +121,7 @@ var _ = BeforeSuite(func(done Done) {
err = scheme.AddToScheme(testScheme)
Expect(err).NotTo(HaveOccurred())
terraformv1beta1.AddToScheme(testScheme)
terraformv1beta2.AddToScheme(testScheme)
crdv1.AddToScheme(testScheme)

View File

@@ -1,20 +1,11 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
controller-gen.kubebuilder.io/version: v0.6.0
creationTimestamp: null
name: configurations.terraform.core.oam.dev
spec:
additionalPrinterColumns:
- JSONPath: .status.state
name: STATE
type: string
- JSONPath: .metadata.creationTimestamp
name: AGE
type: date
group: terraform.core.oam.dev
names:
kind: Configuration
@@ -22,96 +13,294 @@ spec:
plural: configurations
singular: configuration
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
description: Configuration is the Schema for the configurations API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ConfigurationSpec defines the desired state of Configuration
properties:
JSON:
description: JSON is the Terraform JSON syntax configuration
type: string
backend:
description: Backend stores the state in a Kubernetes secret with locking
done using a Lease resource. TODO(zzxwill) If a backend exists in
HCL/JSON, this can be optional. Currently, if Backend is not set by
users, it still will set by the controller, ignoring the settings
in HCL/JSON backend
properties:
inClusterConfig:
description: InClusterConfig Used to authenticate to the cluster
from inside a pod. Only `true` is allowed
type: boolean
secretSuffix:
description: 'SecretSuffix used when creating secrets. Secrets will
be named in the format: tfstate-{workspace}-{secretSuffix}'
type: string
type: object
hcl:
description: HCL is the Terraform HCL type configuration
type: string
variable:
type: object
x-kubernetes-preserve-unknown-fields: true
writeConnectionSecretToRef:
description: WriteConnectionSecretToReference specifies the namespace
and name of a Secret to which any connection details for this managed
resource should be written. Connection details frequently include
the endpoint, username, and password required to connect to the managed
resource.
properties:
name:
description: Name of the secret.
type: string
namespace:
description: Namespace of the secret.
type: string
required:
- name
type: object
type: object
status:
description: ConfigurationStatus defines the observed state of Configuration
properties:
message:
type: string
outputs:
additionalProperties:
properties:
type:
type: string
value:
type: string
type: object
type: object
state:
description: A ResourceState represents the status of a resource
type: string
type: object
type: object
version: v1beta1
versions:
- name: v1beta1
served: true
storage: true
- additionalPrinterColumns:
- jsonPath: .status.apply.state
name: STATE
type: string
- jsonPath: .metadata.creationTimestamp
name: AGE
type: date
name: v1beta1
schema:
openAPIV3Schema:
description: Configuration is the Schema for the configurations API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ConfigurationSpec defines the desired state of Configuration
properties:
JSON:
description: 'JSON is the Terraform JSON syntax configuration. Deprecated:
after v0.3.1, use HCL instead.'
type: string
backend:
description: Backend stores the state in a Kubernetes secret with
locking done using a Lease resource. TODO(zzxwill) If a backend
exists in HCL/JSON, this can be optional. Currently, if Backend
is not set by users, it still will set by the controller, ignoring
the settings in HCL/JSON backend
properties:
inClusterConfig:
description: InClusterConfig Used to authenticate to the cluster
from inside a pod. Only `true` is allowed
type: boolean
secretSuffix:
description: 'SecretSuffix used when creating secrets. Secrets
will be named in the format: tfstate-{workspace}-{secretSuffix}'
type: string
type: object
deleteResource:
default: true
description: DeleteResource will determine whether provisioned cloud
resources will be deleted when CR is deleted
type: boolean
hcl:
description: HCL is the Terraform HCL type configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
type: string
providerRef:
description: ProviderReference specifies the reference to Provider
properties:
name:
description: Name of the referenced object.
type: string
namespace:
default: default
description: Namespace of the referenced object.
type: string
required:
- name
type: object
region:
description: Region is cloud provider's region. It will override the
region in the region field of ProviderReference
type: string
remote:
description: Remote is a git repo which contains hcl files. Currently,
only public git repos are supported.
type: string
variable:
type: object
x-kubernetes-preserve-unknown-fields: true
writeConnectionSecretToRef:
description: WriteConnectionSecretToReference specifies the namespace
and name of a Secret to which any connection details for this managed
resource should be written. Connection details frequently include
the endpoint, username, and password required to connect to the
managed resource.
properties:
name:
description: Name of the secret.
type: string
namespace:
description: Namespace of the secret.
type: string
required:
- name
type: object
type: object
status:
description: ConfigurationStatus defines the observed state of Configuration
properties:
apply:
description: ConfigurationApplyStatus is the status for Configuration
apply
properties:
message:
type: string
outputs:
additionalProperties:
description: Property is the property for an output
properties:
type:
type: string
value:
type: string
type: object
type: object
state:
description: A ConfigurationState represents the status of a resource
type: string
type: object
destroy:
description: ConfigurationDestroyStatus is the status for Configuration
destroy
properties:
message:
type: string
state:
description: A ConfigurationState represents the status of a resource
type: string
type: object
observedGeneration:
description: observedGeneration is the most recent generation observed
for this Configuration. It corresponds to the Configuration's generation,
which is updated on mutation by the API Server.
format: int64
type: integer
type: object
type: object
served: true
storage: false
subresources:
status: {}
- additionalPrinterColumns:
- jsonPath: .status.apply.state
name: STATE
type: string
- jsonPath: .metadata.creationTimestamp
name: AGE
type: date
name: v1beta2
schema:
openAPIV3Schema:
description: Configuration is the Schema for the configurations API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: ConfigurationSpec defines the desired state of Configuration
properties:
backend:
description: Backend stores the state in a Kubernetes secret with
locking done using a Lease resource. TODO(zzxwill) If a backend
exists in HCL/JSON, this can be optional. Currently, if Backend
is not set by users, it still will set by the controller, ignoring
the settings in HCL/JSON backend
properties:
inClusterConfig:
description: InClusterConfig Used to authenticate to the cluster
from inside a pod. Only `true` is allowed
type: boolean
secretSuffix:
description: 'SecretSuffix used when creating secrets. Secrets
will be named in the format: tfstate-{workspace}-{secretSuffix}'
type: string
type: object
customRegion:
description: Region is cloud provider's region. It will override the
region in the region field of ProviderReference
type: string
deleteResource:
default: true
description: DeleteResource will determine whether provisioned cloud
resources will be deleted when CR is deleted
type: boolean
hcl:
description: HCL is the Terraform HCL type configuration
type: string
path:
description: Path is the sub-directory of remote git repository.
type: string
providerRef:
description: ProviderReference specifies the reference to Provider
properties:
name:
description: Name of the referenced object.
type: string
namespace:
default: default
description: Namespace of the referenced object.
type: string
required:
- name
type: object
remote:
description: Remote is a git repo which contains hcl files. Currently,
only public git repos are supported.
type: string
variable:
type: object
x-kubernetes-preserve-unknown-fields: true
writeConnectionSecretToRef:
description: WriteConnectionSecretToReference specifies the namespace
and name of a Secret to which any connection details for this managed
resource should be written. Connection details frequently include
the endpoint, username, and password required to connect to the
managed resource.
properties:
name:
description: Name of the secret.
type: string
namespace:
description: Namespace of the secret.
type: string
required:
- name
type: object
type: object
status:
description: ConfigurationStatus defines the observed state of Configuration
properties:
apply:
description: ConfigurationApplyStatus is the status for Configuration
apply
properties:
message:
type: string
outputs:
additionalProperties:
description: Property is the property for an output
properties:
value:
type: string
type: object
type: object
state:
description: A ConfigurationState represents the status of a resource
type: string
type: object
destroy:
description: ConfigurationDestroyStatus is the status for Configuration
destroy
properties:
message:
type: string
state:
description: A ConfigurationState represents the status of a resource
type: string
type: object
observedGeneration:
description: observedGeneration is the most recent generation observed
for this Configuration. It corresponds to the Configuration's generation,
which is updated on mutation by the API Server. If ObservedGeneration
equals Generation, and State is Available, the value of Outputs
is latest
format: int64
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
storedVersions: []

View File

@@ -37,7 +37,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
oamtypes "github.com/oam-dev/kubevela/apis/types"

View File

@@ -317,7 +317,7 @@ func (val *Value) LookupValue(paths ...string) (*Value, error) {
func (val *Value) LookupByScript(script string) (*Value, error) {
var outputKey = "zz_output__"
script = strings.TrimSpace(script)
scriptFile, err := parser.ParseFile("-", script)
scriptFile, err := parser.ParseFile("-", script, parser.ParseComments)
if err != nil {
return nil, errors.WithMessage(err, "parse script")
}
@@ -327,7 +327,7 @@ func (val *Value) LookupByScript(script string) (*Value, error) {
return nil, err
}
rawFile, err := parser.ParseFile("-", raw)
rawFile, err := parser.ParseFile("-", raw, parser.ParseComments)
if err != nil {
return nil, errors.WithMessage(err, "parse script")
}

View File

@@ -597,6 +597,23 @@ func TestLookupByScript(t *testing.T) {
}{
{
src: `
traits: {
ingress: {
// +patchKey=name
test: [{name: "main", image: "busybox"}]
}
}
`,
script: `traits["ingress"]`,
expect: `// +patchKey=name
test: [{
name: "main"
image: "busybox"
}]
`,
},
{
src: `
apply: containers: [{name: "main", image: "busybox"}]
`,
script: `apply.containers[0].image`,

View File

@@ -20,7 +20,9 @@ import (
"context"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
@@ -89,7 +91,7 @@ func (cache *resourceCache) get(ctx context.Context, mr v1beta1.ManagedResource)
}
if !entry.loaded {
if err := cache.cli.Get(multicluster.ContextWithClusterName(ctx, mr.Cluster), mr.NamespacedName(), entry.obj); err != nil {
if multicluster.IsNotFoundOrClusterNotExists(err) {
if multicluster.IsNotFoundOrClusterNotExists(err) || meta.IsNoMatchError(err) || runtime.IsNotRegisteredError(err) {
entry.exists = false
} else {
entry.err = errors.Wrapf(err, "failed to get resource %s", key)

View File

@@ -37,7 +37,7 @@ import (
"github.com/hashicorp/hcl/v2/hclparse"
clustergatewayapi "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
"github.com/oam-dev/terraform-config-inspect/tfconfig"
terraformv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
kruise "github.com/openkruise/kruise-api/apps/v1alpha1"
errors2 "github.com/pkg/errors"
certmanager "github.com/wonderflow/cert-manager-api/pkg/apis/certmanager/v1"
@@ -93,7 +93,7 @@ func init() {
_ = istioclientv1beta1.AddToScheme(Scheme)
_ = certmanager.AddToScheme(Scheme)
_ = kruise.AddToScheme(Scheme)
_ = terraformv1beta1.AddToScheme(Scheme)
_ = terraformapi.AddToScheme(Scheme)
_ = ocmclusterv1alpha1.Install(Scheme)
_ = ocmclusterv1.Install(Scheme)
_ = ocmworkv1.Install(Scheme)

View File

@@ -0,0 +1,160 @@
/*
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 config
import (
"context"
"fmt"
"strings"
tcv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
"github.com/pkg/errors"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"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/apiserver/model"
)
const (
errAuthenticateProvider = "failed to authenticate Terraform cloud provider %s for %s"
errProviderExists = "terraform provider %s for %s already exists"
errDeleteProvider = "failed to delete Terraform Provider %s"
errCouldNotDeleteProvider = "the Terraform Provider %s could not be disabled because it was created by enabling a Terraform provider or was manually created"
errCheckProviderExistence = "failed to check if Terraform Provider %s exists"
)
// UIParam is the UI parameters from VelaUX for the application
type UIParam struct {
Alias string `json:"alias"`
Description string `json:"description"`
Project string `json:"project"`
}
// CreateApplication creates a new application for the config
func CreateApplication(ctx context.Context, k8sClient client.Client, name, componentType, properties string, ui UIParam) error {
if strings.HasPrefix(componentType, types.TerraformComponentPrefix) {
existed, err := IsTerraformProviderExisted(ctx, k8sClient, name)
if err != nil {
return errors.Wrapf(err, errAuthenticateProvider, name, componentType)
}
if existed {
return fmt.Errorf(errProviderExists, name, componentType)
}
}
app := v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: types.DefaultKubeVelaNS,
Annotations: map[string]string{
types.AnnotationConfigAlias: ui.Alias,
types.AnnotationConfigDescription: ui.Description,
},
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigType: componentType,
types.LabelConfigProject: ui.Project,
},
},
Spec: v1beta1.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: name,
Type: componentType,
Properties: &runtime.RawExtension{Raw: []byte(properties)},
},
},
},
}
return k8sClient.Create(ctx, &app)
}
// DeleteApplication deletes a config application, including a Terraform provider.
// For a Terraform Provider, it can come from
// 1). manually created a Terraform Provider object, like https://github.com/oam-dev/terraform-controller/blob/master/getting-started.md#aws
// 2). by enabling a Terraform provider addon in version older than v1.3.0
// 3). by create a Terraform provider via `vela provider add`
// 4). by VelaUX
// We will only target on deleting a provider which comes from 3) or 4) as for 1), it can be easily delete by hand, and
// for 2), it will be recreated by the addon.
func DeleteApplication(ctx context.Context, k8sClient client.Client, name string, isTerraformProvider bool) error {
if isTerraformProvider {
existed, err := IsTerraformProviderExisted(ctx, k8sClient, name)
if err != nil {
return errors.Wrapf(err, errCheckProviderExistence, name)
}
if existed {
// In version 1.3.0, we used `providerAppName` as the name of the application to create a provider, but
// in version 1.3.1, a config name is the config name, ie, the provider name. To keep backward compatibility,
// we need to check the legacy name and the current name of the application.
legacyName := fmt.Sprintf("%s-%s", types.ProviderAppPrefix, name)
err1 := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: legacyName}, &v1beta1.Application{})
if err1 == nil {
name = legacyName
}
if err1 != nil {
if kerrors.IsNotFound(err1) {
err2 := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, &v1beta1.Application{})
if err2 != nil {
if kerrors.IsNotFound(err2) {
return fmt.Errorf(errCouldNotDeleteProvider, name)
}
return fmt.Errorf(errDeleteProvider, name)
}
}
return fmt.Errorf(errDeleteProvider, name)
}
}
}
a := &v1beta1.Application{}
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, a); err != nil {
return err
}
if err := k8sClient.Delete(ctx, a); err != nil {
return err
}
return nil
}
// ListTerraformProviders returns a list of Terraform providers.
func ListTerraformProviders(ctx context.Context, k8sClient client.Client) ([]tcv1beta1.Provider, error) {
l := &tcv1beta1.ProviderList{}
if err := k8sClient.List(ctx, l, client.InNamespace(types.ProviderNamespace)); err != nil {
return nil, err
}
return l.Items, nil
}
// IsTerraformProviderExisted returns whether a Terraform provider exists.
func IsTerraformProviderExisted(ctx context.Context, k8sClient client.Client, name string) (bool, error) {
l, err := ListTerraformProviders(ctx, k8sClient)
if err != nil {
return false, err
}
for _, p := range l {
if p.Name == name {
return true, nil
}
}
return false, nil
}

View File

@@ -0,0 +1,31 @@
/*
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 config
import (
v1 "k8s.io/api/core/v1"
"github.com/oam-dev/kubevela/apis/types"
)
// ProjectMatched will check whether a config secret can be used in a given project
func ProjectMatched(s *v1.Secret, project string) bool {
if s.Labels[types.LabelConfigProject] == "" || s.Labels[types.LabelConfigProject] == project {
return true
}
return false
}

View File

@@ -0,0 +1,97 @@
/*
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 config
import (
"testing"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"github.com/oam-dev/kubevela/apis/types"
)
var ResponseString = "Hello HTTP Get."
func TestMatchProject(t *testing.T) {
s := runtime.NewScheme()
corev1.AddToScheme(s)
secret1 := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "s1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigProject: "p1",
},
},
}
secret2 := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "s2",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigProject: "",
},
},
}
type args struct {
secret *corev1.Secret
project string
}
type want struct {
matched bool
}
testcases := []struct {
name string
args args
want want
}{
{
name: "matched",
args: args{
project: "p99",
secret: secret1,
},
want: want{
matched: false,
},
},
{
name: "not matched",
args: args{
project: "p99",
secret: secret2,
},
want: want{
matched: true,
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
got := ProjectMatched(tc.args.secret, tc.args.project)
assert.Equal(t, tc.want.matched, got)
})
}
}

View File

@@ -28,8 +28,8 @@ import (
batchv1 "k8s.io/api/batch/v1"
batchv1beta1 "k8s.io/api/batch/v1beta1"
corev1 "k8s.io/api/core/v1"
networkv1beta1 "k8s.io/api/networking/v1beta1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -402,16 +402,32 @@ func (c *HelmReleaseCollector) CollectServices(ctx context.Context, cluster stri
}
// CollectIngress collect ingress of HelmRelease
func (c *HelmReleaseCollector) CollectIngress(ctx context.Context, cluster string) ([]networkv1beta1.Ingress, error) {
cctx := multicluster.ContextWithClusterName(ctx, cluster)
func (c *HelmReleaseCollector) CollectIngress(ctx context.Context, cluster string) ([]unstructured.Unstructured, error) {
clusterCTX := multicluster.ContextWithClusterName(ctx, cluster)
listOptions := []client.ListOption{
client.MatchingLabels(c.matchLabels),
}
var ingreses networkv1beta1.IngressList
if err := c.cli.List(cctx, &ingreses, listOptions...); err != nil {
return nil, err
var ingresses = new(unstructured.UnstructuredList)
ingresses.SetGroupVersionKind(schema.GroupVersionKind{
Group: "networking.k8s.io",
Version: "v1beta1",
Kind: "IngressList",
})
if err := c.cli.List(clusterCTX, ingresses, listOptions...); err != nil {
if meta.IsNoMatchError(err) {
ingresses.SetGroupVersionKind(schema.GroupVersionKind{
Group: "networking.k8s.io",
Version: "v1",
Kind: "IngressList",
})
if err := c.cli.List(clusterCTX, ingresses, listOptions...); err != nil {
return nil, err
}
} else {
return nil, err
}
}
return ingreses.Items, nil
return ingresses.Items, nil
}
// helmReleasePodCollector collect pods created by helmRelease

View File

@@ -32,6 +32,7 @@ import (
kerrors "k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
@@ -275,14 +276,17 @@ func (h *provider) GeneratorServiceEndpoints(wfctx wfContext.Context, v *value.V
for _, service := range services {
serviceEndpoints = append(serviceEndpoints, generatorFromService(service, selectorNodeIP, cluster, resource.Component, "")...)
}
// only support network/v1beta1
ingress, err := hc.CollectIngress(ctx, resource.Cluster)
if err != nil {
klog.Error(err, "collect ingres by helm release failure", "helmRelease", resource.Name, "namespace", resource.Namespace, "cluster", resource.Cluster)
}
for _, ing := range ingress {
serviceEndpoints = append(serviceEndpoints, generatorFromIngress(ing, cluster, resource.Component)...)
for _, uns := range ingress {
var ingress networkv1beta1.Ingress
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(uns.UnstructuredContent(), &ingress); err != nil {
klog.Errorf("fail to convert unstructured to ingress %s", err.Error())
continue
}
serviceEndpoints = append(serviceEndpoints, generatorFromIngress(ingress, cluster, resource.Component)...)
}
case "SeldonDeployment":
obj := new(unstructured.Unstructured)

View File

@@ -31,14 +31,11 @@ import (
"helm.sh/helm/v3/pkg/strvals"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
"github.com/oam-dev/kubevela/pkg/oam"
"k8s.io/client-go/rest"
"github.com/gosuri/uitable"
"github.com/olekukonko/tablewriter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
types2 "k8s.io/apimachinery/pkg/types"
@@ -111,10 +108,11 @@ func NewAddonListCommand(c common.Args) *cobra.Command {
if err != nil {
return err
}
err = listAddons(context.Background(), k8sClient, "")
table, err := listAddons(context.Background(), k8sClient, "")
if err != nil {
return err
}
fmt.Println(table.String())
return nil
},
}
@@ -131,7 +129,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
Enable addon by:
vela addon enable <addon-name>
Enable addon with specify version:
vela addon enable <addon-name> --version <addon-version>
vela addon enable <addon-name> --version <addon-version>
Enable addon for specific clusters, (local means control plane):
vela addon enable <addon-name> --clusters={local,cluster1,cluster2}
`,
@@ -181,7 +179,7 @@ Enable addon for specific clusters, (local means control plane):
}
}
fmt.Printf("Addon: %s enabled Successfully.\n", name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name, false)
return nil
},
}
@@ -192,25 +190,21 @@ Enable addon for specific clusters, (local means control plane):
}
// AdditionalEndpointPrinter will print endpoints
func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient client.Client, name string) {
endpoints, _ := GetServiceEndpoints(ctx, k8sClient, pkgaddon.Convert2AppName(name), types.DefaultKubeVelaNS, c, Filter{})
if len(endpoints) > 0 {
table := tablewriter.NewWriter(os.Stdout)
table.SetColWidth(100)
table.SetHeader([]string{"Cluster", "Component", "Ref(Kind/Namespace/Name)", "Endpoint"})
for _, endpoint := range endpoints {
table.Append([]string{endpoint.Cluster, endpoint.Component, fmt.Sprintf("%s/%s/%s", endpoint.Ref.Kind, endpoint.Ref.Namespace, endpoint.Ref.Name), endpoint.String()})
}
fmt.Printf("Please access the %s from the following endpoints:\n", name)
table.Render()
func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient client.Client, name string, isUpgrade bool) {
fmt.Printf("Please access the %s from the following endpoints:\n", name)
err := printAppEndpoints(ctx, pkgaddon.Convert2AppName(name), types.DefaultKubeVelaNS, Filter{}, c)
if err != nil {
fmt.Println("Get application endpoints error:", err)
return
}
if name == "velaux" {
fmt.Println(`To check the initialized admin user name and password by:`)
fmt.Println(` vela logs -n vela-system --name apiserver addon-velaux | grep "initialized admin username"`)
if !isUpgrade {
fmt.Println(`To check the initialized admin user name and password by:`)
fmt.Println(` vela logs -n vela-system --name apiserver addon-velaux | grep "initialized admin username"`)
}
fmt.Println(`To open the dashboard directly by port-forward:`)
fmt.Println(` vela port-forward -n vela-system addon-velaux 9082:80`)
fmt.Println(`Select "Cluster: local | Namespace: vela-system | Component: velaux | Kind: Service" from the prompt.`)
fmt.Println(`Select "Cluster: local | Namespace: vela-system | Kind: Service | Name: velaux" from the prompt.`)
fmt.Println(`Please refer to https://kubevela.io/docs/reference/addons/velaux for more VelaUX addon installation and visiting method.`)
}
}
@@ -226,7 +220,7 @@ func NewAddonUpgradeCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Co
Upgrade addon by:
vela addon upgrade <addon-name>
Upgrade addon with specify version:
vela addon upgrade <addon-name> --version <addon-version>
vela addon upgrade <addon-name> --version <addon-version>
Upgrade addon for specific clusters, (local means control plane):
vela addon upgrade <addon-name> --clusters={local,cluster1,cluster2}
`,
@@ -284,7 +278,7 @@ Upgrade addon for specific clusters, (local means control plane):
}
fmt.Printf("Addon: %s\n enabled Successfully.", name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name, true)
return nil
},
}
@@ -295,7 +289,7 @@ Upgrade addon for specific clusters, (local means control plane):
func parseAddonArgsToMap(args []string) (map[string]interface{}, error) {
res := map[string]interface{}{}
for _, arg := range args {
if err := strvals.ParseIntoString(arg, res); err != nil {
if err := strvals.ParseInto(arg, res); err != nil {
return nil, err
}
}
@@ -449,15 +443,15 @@ func generateAddonInfo(name string, status pkgaddon.Status) string {
return res
}
func listAddons(ctx context.Context, clt client.Client, registry string) error {
func listAddons(ctx context.Context, clt client.Client, registry string) (*uitable.Table, error) {
var addons []*pkgaddon.UIData
var err error
registryDS := pkgaddon.NewRegistryDataStore(clt)
registries, err := registryDS.ListRegistries(ctx)
if err != nil {
return err
return nil, err
}
onlineAddon := map[string]bool{}
for _, r := range registries {
if registry != "" && r.Name != registry {
continue
@@ -486,31 +480,38 @@ func listAddons(ctx context.Context, clt client.Client, registry string) error {
table := uitable.New()
table.AddRow("NAME", "REGISTRY", "DESCRIPTION", "AVAILABLE-VERSIONS", "STATUS")
// get locally installed addons first
locallyInstalledAddons := map[string]bool{}
appList := v1beta1.ApplicationList{}
if err := clt.List(ctx, &appList, client.MatchingLabels{oam.LabelAddonRegistry: pkgaddon.LocalAddonRegistryName}); err != nil {
return table, err
}
for _, app := range appList.Items {
labels := app.GetLabels()
addonName := labels[oam.LabelAddonName]
addonVersion := labels[oam.LabelAddonVersion]
table.AddRow(addonName, app.GetLabels()[oam.LabelAddonRegistry], "", genAvailableVersionInfo([]string{addonVersion}, addonVersion), statusEnabled)
locallyInstalledAddons[addonName] = true
}
for _, addon := range addons {
// if the addon with same name has already installed locally, display the registry one as not installed
if locallyInstalledAddons[addon.Name] {
table.AddRow(addon.Name, addon.RegistryName, addon.Description, addon.AvailableVersions, "disabled")
continue
}
status, err := pkgaddon.GetAddonStatus(ctx, clt, addon.Name)
if err != nil {
return err
return table, err
}
statusRow := status.AddonPhase
if len(status.InstalledVersion) != 0 {
statusRow += fmt.Sprintf(" (%s)", status.InstalledVersion)
}
table.AddRow(addon.Name, addon.RegistryName, addon.Description, genAvailableVersionInfo(addon.AvailableVersions, status), statusRow)
onlineAddon[addon.Name] = true
table.AddRow(addon.Name, addon.RegistryName, addon.Description, genAvailableVersionInfo(addon.AvailableVersions, status.InstalledVersion), statusRow)
}
appList := v1alpha2.ApplicationList{}
if err := clt.List(ctx, &appList, client.MatchingLabels{oam.LabelAddonRegistry: pkgaddon.LocalAddonRegistryName}); err != nil {
return err
}
for _, app := range appList.Items {
addonName := app.GetLabels()[oam.LabelAddonName]
if onlineAddon[addonName] {
continue
}
table.AddRow(addonName, app.GetLabels()[oam.LabelAddonRegistry], "", statusEnabled)
}
fmt.Println(table.String())
return nil
return table, nil
}
func waitApplicationRunning(k8sClient client.Client, addonName string) error {
@@ -546,13 +547,13 @@ func waitApplicationRunning(k8sClient client.Client, addonName string) error {
// generate the available version
// this func put the installed version as the first version and keep the origin order
// print ... if available version too much
func genAvailableVersionInfo(versions []string, status pkgaddon.Status) string {
func genAvailableVersionInfo(versions []string, installedVersion string) string {
var v []string
// put installed-version as the first version and keep the origin order
if len(status.InstalledVersion) != 0 {
if len(installedVersion) != 0 {
for i, version := range versions {
if version == status.InstalledVersion {
if version == installedVersion {
v = append(v, version)
versions = append(versions[:i], versions[i+1:]...)
}
@@ -568,7 +569,7 @@ func genAvailableVersionInfo(versions []string, status pkgaddon.Status) string {
res += "..."
break
}
if version == status.InstalledVersion {
if version == installedVersion {
col := color.New(color.Bold, color.FgGreen)
res += col.Sprintf("%s", version)
} else {

View File

@@ -0,0 +1,125 @@
/*
Copyright 2021 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 cli
import (
"context"
"fmt"
"time"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/fatih/color"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
pkgaddon "github.com/oam-dev/kubevela/pkg/addon"
"github.com/gosuri/uitable"
)
var _ = Describe("Output of listing addons tests", func() {
// Output of function listAddons to test
var actualTable *uitable.Table
// getRowsByName extracts every rows with its NAME matching name
getRowsByName := func(name string) []*uitable.Row {
matchedRows := []*uitable.Row{}
for _, row := range actualTable.Rows {
// Check column NAME(0) = name
if row.Cells[0].Data == name {
matchedRows = append(matchedRows, row)
}
}
return matchedRows
}
BeforeEach(func() {
// Prepare KubeVela registry
reg := &pkgaddon.Registry{
Name: "KubeVela",
Helm: &pkgaddon.HelmSource{
URL: "https://addons.kubevela.net",
},
}
ds := pkgaddon.NewRegistryDataStore(k8sClient)
Expect(ds.AddRegistry(context.Background(), *reg)).To(Succeed())
})
JustBeforeEach(func() {
// Print addon list to table for later comparison
ret, err := listAddons(context.Background(), k8sClient, "")
Expect(err).Should(BeNil())
actualTable = ret
})
When("there is no addons installed", func() {
It("should not have any enabled addon", func() {
Expect(actualTable.Rows).ToNot(HaveLen(0))
for idx, row := range actualTable.Rows {
// Skip header
if idx == 0 {
continue
}
// Check column STATUS(4) = disabled
Expect(row.Cells[4].Data).To(Equal("disabled"))
}
})
})
When("there is locally installed addons", func() {
BeforeEach(func() {
// Install fluxcd locally
fluxcd := v1beta1.Application{}
err := yaml.Unmarshal([]byte(fluxcdYaml), &fluxcd)
Expect(err).Should(BeNil())
Expect(k8sClient.Create(context.Background(), &fluxcd)).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
})
It("should print fluxcd addon as local", func() {
matchedRows := getRowsByName("fluxcd")
Expect(matchedRows).ToNot(HaveLen(0))
// Only use first row (local first), check column REGISTRY(1) = local
Expect(matchedRows[0].Cells[1].Data).To(Equal("local"))
Eventually(func() error {
matchedRows = getRowsByName("fluxcd")
// Check column STATUS(4) = enabled
if matchedRows[0].Cells[4].Data != "enabled" {
return fmt.Errorf("fluxcd is not enabled yet")
}
// Check column AVAILABLE-VERSIONS(3) = 1.1.0
if versionString := matchedRows[0].Cells[3].Data; versionString != fmt.Sprintf("[%s]", color.New(color.Bold, color.FgGreen).Sprintf("1.1.0")) {
return fmt.Errorf("fluxcd version string is incorrect: %s", versionString)
}
return nil
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
})
It("should print fluxcd in the registry as disabled", func() {
matchedRows := getRowsByName("fluxcd")
// There should be a local one and a registry one
Expect(len(matchedRows)).To(Equal(2))
// The registry one should be disabled
Expect(matchedRows[1].Cells[1].Data).To(Equal("KubeVela"))
Expect(matchedRows[1].Cells[4].Data).To(Equal("disabled"))
})
})
})

View File

@@ -70,6 +70,20 @@ func TestParseMap(t *testing.T) {
},
nilError: true,
},
{
args: []string{"local=true"},
res: map[string]interface{}{
"local": true,
},
nilError: true,
},
{
args: []string{"replicas=3"},
res: map[string]interface{}{
"replicas": int64(3),
},
nilError: true,
},
}
for _, s := range testcase {
r, err := parseAddonArgsToMap(s.args)
@@ -211,7 +225,7 @@ func TestGenerateAvailableVersions(t *testing.T) {
},
}
for _, s := range testcases {
re := genAvailableVersionInfo(s.c.versions, pkgaddon.Status{InstalledVersion: s.c.inVersion})
re := genAvailableVersionInfo(s.c.versions, s.c.inVersion)
assert.Equal(t, re, s.res)
}
}

View File

@@ -349,7 +349,7 @@ func generateTerraformTypedComponentDefinition(cmd *cobra.Command, name, kind, p
Spec: v1beta1.ComponentDefinitionSpec{
Workload: commontype.WorkloadTypeDescriptor{
Definition: commontype.WorkloadGVK{
APIVersion: "terraform.core.oam.dev/v1beta1",
APIVersion: "terraform.core.oam.dev/v1beta2",
Kind: "Configuration",
},
},

View File

@@ -300,7 +300,7 @@ spec:
namespace: default
workload:
definition:
apiVersion: terraform.core.oam.dev/v1beta1
apiVersion: terraform.core.oam.dev/v1beta2
kind: Configuration
status: {}
`,
@@ -327,7 +327,7 @@ spec:
type: remote
workload:
definition:
apiVersion: terraform.core.oam.dev/v1beta1
apiVersion: terraform.core.oam.dev/v1beta2
kind: Configuration
status: {}`,
},

View File

@@ -27,27 +27,22 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
k8stypes "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
coreapi "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/definition"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/config"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
"github.com/oam-dev/kubevela/references/plugins"
)
const (
// ProviderNamespace is the namespace of Terraform Cloud Provider
ProviderNamespace = "default"
providerNameParam = "name"
providerNameParam = "name"
errAuthenticateProvider = "failed to authenticate Terraform cloud provider %s"
)
// NewProviderCommand create `addon` command
@@ -193,38 +188,14 @@ func prepareProviderAddSubCommand(c common.Args, ioStreams cmdutil.IOStreams) ([
}
data, err := json.Marshal(properties)
if err != nil {
return fmt.Errorf("failed to authenticate Terraform cloud provider %s", providerType)
return fmt.Errorf(errAuthenticateProvider, providerType)
}
providerAppName := fmt.Sprintf("config-terraform-provider-%s", name)
a := &v1beta1.Application{}
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: providerAppName}, a); err != nil {
if kerrors.IsNotFound(err) {
a = &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: providerAppName,
Namespace: types.DefaultKubeVelaNS,
},
Spec: v1beta1.ApplicationSpec{
Components: []coreapi.ApplicationComponent{
{
Name: providerAppName,
Type: providerType,
Properties: &runtime.RawExtension{
Raw: data,
},
},
},
},
}
if err := k8sClient.Create(ctx, a); err != nil {
return fmt.Errorf("failed to authenticate Terraform cloud provider %s", providerType)
}
ioStreams.Infof("Successfully authenticate provider %s for %s\n", name, providerType)
return nil
}
return fmt.Errorf("failed to authenticate Terraform cloud provider %s", providerType)
if err := config.CreateApplication(ctx, k8sClient, name, providerType, string(data), config.UIParam{}); err != nil {
return fmt.Errorf(errAuthenticateProvider, providerType)
}
return fmt.Errorf("terraform provider %s for %s already exists", name, providerType)
ioStreams.Infof("Successfully authenticate provider %s for %s\n", name, providerType)
return nil
}
cmds[i] = cmd
}
@@ -259,14 +230,14 @@ func listProviders(ctx context.Context, k8sClient client.Client, ioStreams cmdut
currentProviders []tcv1beta1.Provider
legacyProviders []tcv1beta1.Provider
)
tcProviders := &tcv1beta1.ProviderList{}
// client.MatchingLabels{: }
if err := k8sClient.List(ctx, tcProviders, client.InNamespace(ProviderNamespace)); err != nil {
l, err := config.ListTerraformProviders(ctx, k8sClient)
if err != nil {
return errors.Wrap(err, "failed to retrieve providers")
}
for _, p := range tcProviders.Items {
if p.Labels["config.oam.dev/type"] == types.TerraformProvider {
for _, p := range l {
// The first condition matches the providers created by `vela provider` in 1.3.2 and earlier.
if p.Labels[types.LabelConfigType] == types.TerraformProvider || p.Labels[types.LabelConfigCatalog] == types.VelaCoreConfig {
currentProviders = append(currentProviders, p)
} else {
// if not labeled, the provider is manually created or created by `vela addon enable`.
@@ -429,15 +400,8 @@ func prepareProviderDeleteSubCommand(c common.Args, ioStreams cmdutil.IOStreams)
if err != nil || name == "" {
return fmt.Errorf("must specify a name for the Terraform Cloud Provider %s", providerType)
}
providerAppName := fmt.Sprintf("config-terraform-provider-%s", name)
a := &v1beta1.Application{}
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: providerAppName}, a); err != nil {
if kerrors.IsNotFound(err) {
return fmt.Errorf("provider %s for %s does not exist", name, providerType)
}
}
if err := k8sClient.Delete(ctx, a); err != nil {
return err
if err := config.DeleteApplication(ctx, k8sClient, name, true); err != nil {
return errors.Wrapf(err, "failed to delete Terraform Cloud Provider %s", name)
}
ioStreams.Infof("Successfully delete provider %s for %s\n", name, providerType)
return nil

View File

@@ -34,7 +34,7 @@ import (
"github.com/oam-dev/kubevela/pkg/utils/util"
)
func TestLlistProviders(t *testing.T) {
func TestListProviders(t *testing.T) {
ctx := context.Background()
type args struct {
k8sClient client.Client

View File

@@ -124,7 +124,7 @@ func NewAppStatusCommand(c common.Args, order string, ioStreams cmdutil.IOStream
f := Filter{
Component: component,
}
return printAppEndpoints(ctx, newClient, appName, namespace, f, c)
return printAppEndpoints(ctx, appName, namespace, f, c)
}
return printAppStatus(ctx, newClient, ioStreams, appName, namespace, cmd, c)
},
@@ -163,7 +163,15 @@ func printAppStatus(_ context.Context, c client.Client, ioStreams cmdutil.IOStre
return loopCheckStatus(c, ioStreams, appName, namespace)
}
func printAppEndpoints(ctx context.Context, client client.Client, appName string, namespace string, f Filter, velaC common.Args) error {
func printAppEndpoints(ctx context.Context, appName string, namespace string, f Filter, velaC common.Args) error {
config, err := velaC.GetConfig()
if err != nil {
return err
}
client, err := multicluster.Initialize(config, false)
if err != nil {
return err
}
endpoints, err := GetServiceEndpoints(ctx, client, appName, namespace, velaC, f)
if err != nil {
return err
@@ -172,6 +180,9 @@ func printAppEndpoints(ctx context.Context, client client.Client, appName string
table.SetColWidth(100)
table.SetHeader([]string{"Cluster", "Component", "Ref(Kind/Namespace/Name)", "Endpoint"})
for _, endpoint := range endpoints {
if endpoint.Cluster == "" {
endpoint.Cluster = multicluster.ClusterLocalName
}
table.Append([]string{endpoint.Cluster, endpoint.Component, fmt.Sprintf("%s/%s/%s", endpoint.Ref.Kind, endpoint.Ref.Namespace, endpoint.Ref.Name), endpoint.String()})
}
table.Render()

View File

@@ -237,7 +237,7 @@ func PrintInstalledTraitDef(c common2.Args, io cmdutil.IOStreams, filter filterF
}
capa, err := ParseCapability(dm, data)
if err != nil {
io.Errorf("error parsing capability: %s\n", td.Name)
io.Errorf("error parsing capability: %s (message: %s)\n", td.Name, err.Error())
continue
}
if filter != nil && !filter(capa) {

View File

@@ -70,7 +70,9 @@ metadata:
name: addon-fluxcd
namespace: vela-system
labels:
addons.oam.dev/name: fluxcd
addons.oam.dev/name: fluxcd
addons.oam.dev/registry: local
addons.oam.dev/version: 1.1.0
spec:
components:
- name: ns-flux-system

View File

@@ -23,7 +23,6 @@ import (
"k8s.io/apimachinery/pkg/runtime"
apitypes "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -33,6 +32,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
velacmd "github.com/oam-dev/kubevela/pkg/cmd"
cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
"github.com/oam-dev/kubevela/pkg/component"
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application"
"github.com/oam-dev/kubevela/pkg/controller/utils"

View File

@@ -84,7 +84,7 @@ func GetServiceEndpoints(ctx context.Context, client client.Client, appName stri
Endpoints []querytypes.ServiceEndpoint `json:"endpoints"`
Error string `json:"error"`
}{}
if err := queryValue.CueValue().Decode(&response); err != nil {
if err := queryValue.UnmarshalTo(&response); err != nil {
return nil, err
}
if response.Error != "" {

View File

@@ -88,7 +88,7 @@ var _ = Describe("Addon tests", func() {
It("Addon Terraform is successfully enabled and Terraform application works", func() {
By("Install Addon Terraform")
output, err := exec.Command("bash", "-c", "/tmp/vela addon enable terraform-alibaba ALICLOUD_ACCESS_KEY=xxx ALICLOUD_SECRET_KEY=yyy ALICLOUD_REGION=cn-beijing").Output()
output, err := exec.Command("bash", "-c", "/tmp/vela addon enable terraform-alibaba").Output()
var ee *exec.ExitError
if errors.As(err, &ee) {
fmt.Println("exit code error:", string(ee.Stderr))

View File

@@ -79,6 +79,8 @@ var _ = AfterSuite(func() {
for _, app := range apps.Items {
g.Expect(k8sClient.Delete(context.Background(), app.DeepCopy())).Should(Succeed())
}
err := k8sClient.List(context.Background(), apps)
g.Expect(err, nil)
g.Expect(len(apps.Items)).Should(Equal(0))
}, 3*time.Minute).Should(Succeed())
Eventually(func(g Gomega) {

View File

@@ -33,9 +33,14 @@ template: {
"config.oam.dev/sub-type": "auth"
}
}
type: "kubernetes.io/dockerconfigjson"
stringData: {
if parameter.auth != _|_ {
if parameter.auth != _|_ {
type: "kubernetes.io/dockerconfigjson"
}
if parameter.auth == _|_ {
type: "Opaque"
}
if parameter.auth != _|_ {
stringData: {
".dockerconfigjson": json.Marshal({
"auths": "\(parameter.registry)": {
"username": parameter.auth.username

View File

@@ -175,10 +175,9 @@ template: {
parameter.labels
}
if parameter.addRevisionLabel {
"app.oam.dev/appRevision": context.appRevision
"app.oam.dev/revision": context.revision
}
"app.oam.dev/component": context.name
"app.oam.dev/revision": context.revision
}
if parameter.annotations != _|_ {
annotations: parameter.annotations
@@ -379,7 +378,7 @@ template: {
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
// +ignore
// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
// +usage=If addRevisionLabel is true, the revision label will be added to the underlying pods
addRevisionLabel: *false | bool
// +usage=Commands to run in the container

View File

@@ -39,7 +39,7 @@ template: {
}]
}
if _baseEnv != _|_ {
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar.value}}
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar}}
// +patchStrategy=replace
env: [ for envVar in _baseEnv if _delKeys[envVar.name] == _|_ && !_params.replace {
name: envVar.name
@@ -47,11 +47,15 @@ template: {
value: _params.env[envVar.name]
}
if _params.env[envVar.name] == _|_ {
value: envVar.value
if envVar.value != _|_ {
value: envVar.value
}
if envVar.valueFrom != _|_ {
valueFrom: envVar.valueFrom
}
}
}] + [ for k, v in _params.env if _delKeys[k] == _|_ && (_params.replace || _baseEnvMap[k] == _|_) {
name: k
value: v
v
}]
}
}