Compare commits

..

17 Commits

Author SHA1 Message Date
github-actions[bot]
8bad1fc055 Fix: fix the goroutine leak in http request (#4303)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 559ef83abd)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-07-01 17:55:41 +08:00
barnettZQG
4569850740 Chore: change the acr registry address (#4214) (#4217)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
2022-06-22 14:47:13 +08:00
github-actions[bot]
54477eabf5 Fix: env trait error when existing env exists (#4039)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 2d5a16d45f)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-05-27 21:12:22 +08:00
Zheng Xi Zhou
43bbc97319 Fix: failed to create Provider by CLI (#3964)
Fix #3955

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-05-24 16:42:22 +08:00
github-actions[bot]
cbed2b5cb3 Fix: remove last-applied-config annotation for configmap and secret (#3942)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 4789fa8833)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-05-20 17:09:50 +08:00
github-actions[bot]
8be75545bc Fix: modify the template definition to solve the trait cli error Signed-off-by: Shijie Zhong <zhongsjie@cmbchina.com> (#3880)
Signed-off-by: ZhongsJie <zhongsjie@gmail.com>
(cherry picked from commit b3ef120f95)

Co-authored-by: ZhongsJie <zhongsjie@gmail.com>
2022-05-13 10:54:03 +08:00
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
58 changed files with 3484 additions and 538 deletions

View File

@@ -44,28 +44,24 @@ jobs:
registry: docker.io
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login Alibaba Cloud ACR
- name: Login kubevela private registry
uses: docker/login-action@v1
with:
registry: kubevela-registry.cn-hangzhou.cr.aliyuncs.com
username: ${{ secrets.ACR_USERNAME }}@aliyun-inner.com
registry: ${{ secrets.ACR_DOMAIN }}
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
with:
driver-opts: image=moby/buildkit:master
- name: Build & Pushing vela-core for ACR
run: |
docker build --build-arg GOPROXY=https://proxy.golang.org --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} --build-arg GITVERSION=git-${{ steps.vars.outputs.git_revision }} -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }} .
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
- uses: docker/build-push-action@v2
name: Build & Pushing vela-core for Dockerhub and GHCR
name: Build & Pushing vela-core
with:
context: .
file: Dockerfile
labels: |-
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}
org.opencontainers.image.revision=${{ github.sha }}
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
@@ -75,19 +71,16 @@ jobs:
GOPROXY=https://proxy.golang.org
tags: |-
docker.io/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository }}/vela-core:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/vela-core:${{ steps.get_version.outputs.VERSION }}
${{ secrets.ACR_DOMAIN }}/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
- name: Build & Pushing vela-apiserver for ACR
run: |
docker build --build-arg GOPROXY=https://proxy.golang.org --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} --build-arg GITVERSION=git-${{ steps.vars.outputs.git_revision }} -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }} -f Dockerfile.apiserver .
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
- uses: docker/build-push-action@v2
name: Build & Pushing vela-apiserver for Dockerhub and GHCR
name: Build & Pushing vela-apiserver
with:
context: .
file: Dockerfile.apiserver
labels: |-
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}
org.opencontainers.image.revision=${{ github.sha }}
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
@@ -97,19 +90,16 @@ jobs:
GOPROXY=https://proxy.golang.org
tags: |-
docker.io/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository }}/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
${{ secrets.ACR_DOMAIN }}/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
- name: Build & Pushing vela runtime rollout for ACR
run: |
docker build --build-arg GOPROXY=https://proxy.golang.org --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} --build-arg GITVERSION=git-${{ steps.vars.outputs.git_revision }} -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }} .
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
- uses: docker/build-push-action@v2
name: Build & Pushing runtime rollout for Dockerhub and GHCR
name: Build & Pushing runtime rollout
with:
context: .
file: runtime/rollout/Dockerfile
labels: |-
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}
org.opencontainers.image.revision=${{ github.sha }}
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
@@ -119,7 +109,8 @@ jobs:
GOPROXY=https://proxy.golang.org
tags: |-
docker.io/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository }}/vela-rollout:${{ steps.get_version.outputs.VERSION }}
ghcr.io/${{ github.repository_owner }}/vela-rollout:${{ steps.get_version.outputs.VERSION }}
${{ secrets.ACR_DOMAIN }}/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
publish-charts:
env:

View File

@@ -108,17 +108,22 @@ 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: |
touch ${{ env.VELA_VERSION }} > ./latest_version
./ossutil --config-file .ossutilconfig ./latest_version oss://$BUCKET/binary/vela/latest_version
echo ${{ env.VELA_VERSION }} > ./latest_version
./ossutil --config-file .ossutilconfig cp -u ./latest_version oss://$BUCKET/binary/vela/latest_version
upload-plugin-homebrew:

File diff suppressed because it is too large Load Diff

View File

@@ -106,7 +106,7 @@ spec:
}]
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the commands for multiple containers
containers: [...#PatchParams]
})

View File

@@ -69,7 +69,7 @@ spec:
}]
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the container image for multiple containers
containers: [...#PatchParams]
})

View File

@@ -62,7 +62,8 @@ spec:
}
}
}] + [ for k, v in _params.env if _delKeys[k] == _|_ && (_params.replace || _baseEnvMap[k] == _|_) {
v
name: k
value: v
}]
}
}
@@ -96,7 +97,7 @@ spec:
}]
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the environment variables for multiple containers
containers: [...#PatchParams]
})

File diff suppressed because it is too large Load Diff

View File

@@ -106,7 +106,7 @@ spec:
}]
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the commands for multiple containers
containers: [...#PatchParams]
})

View File

@@ -69,7 +69,7 @@ spec:
}]
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the container image for multiple containers
containers: [...#PatchParams]
})

View File

@@ -62,7 +62,8 @@ spec:
}
}
}] + [ for k, v in _params.env if _delKeys[k] == _|_ && (_params.replace || _baseEnvMap[k] == _|_) {
v
name: k
value: v
}]
}
}
@@ -96,7 +97,7 @@ spec:
}]
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the environment variables for multiple containers
containers: [...#PatchParams]
})

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

@@ -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", 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)
}
}
}
}
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

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

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

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

View File

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

@@ -3897,7 +3897,7 @@ spec:
}]
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the environment variables for multiple containers
containers: [...#PatchParams]
})

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
}

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

@@ -117,7 +117,7 @@ const (
AnnotationAppGeneration = "app.oam.dev/generation"
// AnnotationLastAppliedConfig records the previous configuration of a
// resource for use in a three way diff during a patching apply
// resource for use in a three-way diff during a patching apply
AnnotationLastAppliedConfig = "app.oam.dev/last-applied-configuration"
// AnnotationLastAppliedTime indicates the last applied time

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

@@ -20,19 +20,19 @@ import (
"context"
"fmt"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/oam/util"
"github.com/oam-dev/kubevela/pkg/oam"
"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"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/controller/utils"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
const (
@@ -107,13 +107,42 @@ func loggingApply(msg string, desired client.Object) {
klog.InfoS(msg, "name", d.GetName(), "resource", desired.GetObjectKind().GroupVersionKind().String())
}
// filterRecordForSpecial will filter special object that can reduce the record for "app.oam.dev/last-applied-configuration" annotation.
func filterRecordForSpecial(desired client.Object) bool {
if desired == nil {
return false
}
gvk := desired.GetObjectKind().GroupVersionKind()
gp, kd := gvk.Group, gvk.Kind
if gp == "" {
// group is empty means it's Kubernetes core API, we won't record annotation for Secret and Configmap
if kd == "Secret" || kd == "ConfigMap" {
return false
}
if _, ok := desired.(*corev1.ConfigMap); ok {
return false
}
if _, ok := desired.(*corev1.Secret); ok {
return false
}
}
ann := desired.GetAnnotations()
if ann != nil {
lac := ann[oam.AnnotationLastAppliedConfig]
if lac == "-" || lac == "skip" {
return false
}
}
return true
}
// Apply applies new state to an object or create it if not exist
func (a *APIApplicator) Apply(ctx context.Context, desired client.Object, ao ...ApplyOption) error {
_, err := generateRenderHash(desired)
if err != nil {
return err
}
applyAct := &applyAction{updateAnnotation: true}
applyAct := &applyAction{updateAnnotation: filterRecordForSpecial(desired)}
existing, err := a.createOrGetExisting(ctx, applyAct, a.c, desired, ao...)
if err != nil {
return err

View File

@@ -23,8 +23,10 @@ import (
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -423,3 +425,19 @@ func TestMustBeControlledByApp(t *testing.T) {
})
}
}
func TestFilterSpecialAnn(t *testing.T) {
var cm = &corev1.ConfigMap{}
var sc = &corev1.Secret{}
var dp = &appsv1.Deployment{}
assert.Equal(t, false, filterRecordForSpecial(cm))
assert.Equal(t, false, filterRecordForSpecial(sc))
assert.Equal(t, true, filterRecordForSpecial(dp))
dp.Annotations = map[string]string{oam.AnnotationLastAppliedConfig: "-"}
assert.Equal(t, false, filterRecordForSpecial(dp))
dp.Annotations = map[string]string{oam.AnnotationLastAppliedConfig: "skip"}
assert.Equal(t, false, filterRecordForSpecial(dp))
dp.Annotations = map[string]string{oam.AnnotationLastAppliedConfig: "xxx"}
assert.Equal(t, true, filterRecordForSpecial(dp))
}

View File

@@ -37,7 +37,8 @@ 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"
terraformapiv1 "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"
@@ -49,6 +50,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
metricsV1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
ocmclusterv1alpha1 "open-cluster-management.io/api/cluster/v1alpha1"
ocmworkv1 "open-cluster-management.io/api/work/v1"
@@ -56,8 +58,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/yaml"
metricsV1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
oamcore "github.com/oam-dev/kubevela/apis/core.oam.dev"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
@@ -93,7 +93,8 @@ func init() {
_ = istioclientv1beta1.AddToScheme(Scheme)
_ = certmanager.AddToScheme(Scheme)
_ = kruise.AddToScheme(Scheme)
_ = terraformv1beta1.AddToScheme(Scheme)
_ = terraformapi.AddToScheme(Scheme)
_ = terraformapiv1.AddToScheme(Scheme)
_ = ocmclusterv1alpha1.Install(Scheme)
_ = ocmclusterv1.Install(Scheme)
_ = ocmworkv1.Install(Scheme)

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,7 +31,6 @@ 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"
@@ -109,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
},
}
@@ -129,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}
`,
@@ -192,7 +192,7 @@ 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, isUpgrade bool) {
fmt.Printf("Please access the %s from the following endpoints:\n", name)
err := printAppEndpoints(ctx, k8sClient, pkgaddon.Convert2AppName(name), types.DefaultKubeVelaNS, Filter{}, c)
err := printAppEndpoints(ctx, pkgaddon.Convert2AppName(name), types.DefaultKubeVelaNS, Filter{}, c)
if err != nil {
fmt.Println("Get application endpoints error:", err)
return
@@ -220,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}
`,
@@ -443,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
@@ -480,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 {
@@ -540,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:]...)
}
@@ -562,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

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

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

View File

@@ -17,11 +17,22 @@ limitations under the License.
package cli
import (
"bytes"
"context"
"os"
"strings"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/yaml"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
util2 "github.com/oam-dev/kubevela/pkg/oam/util"
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/util"
"github.com/oam-dev/kubevela/references/common"
)
@@ -43,3 +54,170 @@ func TestTraitsAppliedToAllWorkloads(t *testing.T) {
}
assert.Equal(t, []string{"*"}, common.ConvertApplyTo(trait.AppliesTo, workloads))
}
var _ = Describe("Test trait cli", func() {
When("there are container-image and configmap traits", func() {
BeforeEach(func() {
// Install trait locally
containerImage := v1beta1.TraitDefinition{}
Expect(yaml.Unmarshal([]byte(containerImageYaml), &containerImage)).Should(BeNil())
Expect(k8sClient.Create(context.Background(), &containerImage)).Should(SatisfyAny(BeNil(), util2.AlreadyExistMatcher{}))
configMap := v1beta1.TraitDefinition{}
Expect(yaml.Unmarshal([]byte(configmapYaml), &configMap)).Should(BeNil())
Expect(k8sClient.Create(context.Background(), &configMap)).Should(SatisfyAny(BeNil(), util2.AlreadyExistMatcher{}))
})
It("should not have any err", func() {
arg := common2.Args{}
arg.SetClient(k8sClient)
buffer := bytes.NewBuffer(nil)
ioStreams := util.IOStreams{In: os.Stdin, Out: buffer, ErrOut: buffer}
cmd := NewTraitCommand(arg, ioStreams)
Expect(cmd.Execute()).Should(BeNil())
buf, ok := ioStreams.Out.(*bytes.Buffer)
Expect(ok).Should(BeTrue())
Expect(strings.Contains(buf.String(), "error")).Should(BeFalse())
})
})
})
const (
containerImageYaml = `apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: Set the image of the container.
name: container-image
namespace: vela-system
spec:
appliesToWorkloads:
- '*'
podDisruptive: true
schematic:
cue:
template: |
#PatchParams: {
// +usage=Specify the name of the target container, if not set, use the component name
containerName: *"" | string
// +usage=Specify the image of the container
image: string
// +usage=Specify the image pull policy of the container
imagePullPolicy: *"" | "IfNotPresent" | "Always" | "Never"
}
PatchContainer: {
_params: #PatchParams
name: _params.containerName
_baseContainers: context.output.spec.template.spec.containers
_matchContainers_: [ for _container_ in _baseContainers if _container_.name == name {_container_}]
_baseContainer: *_|_ | {...}
if len(_matchContainers_) == 0 {
err: "container \(name) not found"
}
if len(_matchContainers_) > 0 {
// +patchStrategy=retainKeys
image: _params.image
if _params.imagePullPolicy != "" {
// +patchStrategy=retainKeys
imagePullPolicy: _params.imagePullPolicy
}
}
}
patch: spec: template: spec: {
if parameter.containers == _|_ {
// +patchKey=name
containers: [{
PatchContainer & {_params: {
if parameter.containerName == "" {
containerName: context.name
}
if parameter.containerName != "" {
containerName: parameter.containerName
}
image: parameter.image
imagePullPolicy: parameter.imagePullPolicy
}}
}]
}
if parameter.containers != _|_ {
// +patchKey=name
containers: [ for c in parameter.containers {
if c.containerName == "" {
err: "containerName must be set for containers"
}
if c.containerName != "" {
PatchContainer & {_params: c}
}
}]
}
}
parameter: *#PatchParams | close({
// +usage=Specify the container image for multiple containers
containers: [...#PatchParams]
})
errs: [ for c in patch.spec.template.spec.containers if c.err != _|_ {c.err}]
`
configmapYaml = `apiVersion: core.oam.dev/v1beta1
kind: TraitDefinition
metadata:
annotations:
definition.oam.dev/description: Create/Attach configmaps on K8s pod for your workload which follows the pod spec in path 'spec.template'. This definition is DEPRECATED, please specify configmap in 'storage' instead.
labels:
custom.definition.oam.dev/deprecated: "true"
name: configmap
namespace: vela-system
spec:
appliesToWorkloads:
- '*'
podDisruptive: true
schematic:
cue:
template: |
patch: spec: template: spec: {
containers: [{
// +patchKey=name
volumeMounts: [
for v in parameter.volumes {
{
name: "volume-\(v.name)"
mountPath: v.mountPath
readOnly: v.readOnly
}
},
]
}, ...]
// +patchKey=name
volumes: [
for v in parameter.volumes {
{
name: "volume-\(v.name)"
configMap: name: v.name
}
},
]
}
outputs: {
for v in parameter.volumes {
if v.data != _|_ {
"\(v.name)": {
apiVersion: "v1"
kind: "ConfigMap"
metadata: name: v.name
data: v.data
}
}
}
}
parameter: {
// +usage=Specify mounted configmap names and their mount paths in the container
volumes: [...{
name: string
mountPath: string
readOnly: *false | bool
data?: [string]: string
}]
}
`
)

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

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

@@ -100,7 +100,7 @@ template: {
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the commands for multiple containers
containers: [...#PatchParams]
})

View File

@@ -65,7 +65,7 @@ template: {
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the container image for multiple containers
containers: [...#PatchParams]
})

View File

@@ -55,7 +55,8 @@ template: {
}
}
}] + [ for k, v in _params.env if _delKeys[k] == _|_ && (_params.replace || _baseEnvMap[k] == _|_) {
v
name: k
value: v
}]
}
}
@@ -90,7 +91,7 @@ template: {
}
}
parameter: #PatchParams | close({
parameter: *#PatchParams | close({
// +usage=Specify the environment variables for multiple containers
containers: [...#PatchParams]
})