mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-24 14:54:06 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
978bec227c | ||
|
|
e3e6d57b2a | ||
|
|
813a7534f2 | ||
|
|
f8f75a3b64 | ||
|
|
8bad1fc055 | ||
|
|
4569850740 | ||
|
|
54477eabf5 | ||
|
|
43bbc97319 | ||
|
|
cbed2b5cb3 | ||
|
|
8be75545bc | ||
|
|
d748096f7c | ||
|
|
d4ab93c232 | ||
|
|
37a656a292 | ||
|
|
3c94ac1bc1 | ||
|
|
8499dffcd7 | ||
|
|
30a6e85023 | ||
|
|
11530e2720 | ||
|
|
f53466bc7d | ||
|
|
e8ea8ec48f | ||
|
|
6c4c9bdf7e | ||
|
|
e7930a2da0 |
89
.github/workflows/chart.yaml
vendored
Normal file
89
.github/workflows/chart.yaml
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
name: Publish Chart
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch: { }
|
||||
|
||||
env:
|
||||
BUCKET: ${{ secrets.OSS_BUCKET }}
|
||||
ENDPOINT: ${{ secrets.OSS_ENDPOINT }}
|
||||
ACCESS_KEY: ${{ secrets.OSS_ACCESS_KEY }}
|
||||
ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
||||
ARTIFACT_HUB_REPOSITORY_ID: ${{ secrets.ARTIFACT_HUB_REPOSITORY_ID }}
|
||||
|
||||
jobs:
|
||||
publish-charts:
|
||||
env:
|
||||
HELM_CHARTS_DIR: charts
|
||||
HELM_CHART: charts/vela-core
|
||||
MINIMAL_HELM_CHART: charts/vela-minimal
|
||||
LEGACY_HELM_CHART: legacy/charts/vela-core-legacy
|
||||
VELA_ROLLOUT_HELM_CHART: runtime/rollout/charts
|
||||
LOCAL_OSS_DIRECTORY: .oss/
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Get git revision
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::set-output name=git_revision::$(git rev-parse --short HEAD)"
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: v3.4.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Generate helm doc
|
||||
run: |
|
||||
make helm-doc-gen
|
||||
- name: Prepare legacy chart
|
||||
run: |
|
||||
rsync -r $LEGACY_HELM_CHART $HELM_CHARTS_DIR
|
||||
rsync -r $HELM_CHART/* $LEGACY_HELM_CHART --exclude=Chart.yaml --exclude=crds
|
||||
- name: Prepare vela chart
|
||||
run: |
|
||||
rsync -r $VELA_ROLLOUT_HELM_CHART $HELM_CHARTS_DIR
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
echo ::set-output name=VERSION::${VERSION}
|
||||
- name: Tag helm chart image
|
||||
run: |
|
||||
image_tag=${{ steps.get_version.outputs.VERSION }}
|
||||
chart_version=${{ steps.get_version.outputs.VERSION }}
|
||||
sed -i "s/latest/${image_tag}/g" $HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $MINIMAL_HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $LEGACY_HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $VELA_ROLLOUT_HELM_CHART/values.yaml
|
||||
chart_smever=${chart_version#"v"}
|
||||
sed -i "s/0.1.0/$chart_smever/g" $HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $MINIMAL_HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $LEGACY_HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $VELA_ROLLOUT_HELM_CHART/Chart.yaml
|
||||
- name: Install ossutil
|
||||
run: wget http://gosspublic.alicdn.com/ossutil/1.7.0/ossutil64 && chmod +x ossutil64 && mv ossutil64 ossutil
|
||||
- name: Configure Alibaba Cloud OSSUTIL
|
||||
run: ./ossutil --config-file .ossutilconfig config -i ${ACCESS_KEY} -k ${ACCESS_KEY_SECRET} -e ${ENDPOINT} -c .ossutilconfig
|
||||
- name: sync cloud to local
|
||||
run: ./ossutil --config-file .ossutilconfig sync oss://$BUCKET/core $LOCAL_OSS_DIRECTORY
|
||||
- name: add artifacthub stuff to the repo
|
||||
run: |
|
||||
rsync $HELM_CHART/README.md $LEGACY_HELM_CHART/README.md
|
||||
rsync $HELM_CHART/README.md $VELA_ROLLOUT_HELM_CHART/README.md
|
||||
sed -i "s/ARTIFACT_HUB_REPOSITORY_ID/$ARTIFACT_HUB_REPOSITORY_ID/g" hack/artifacthub/artifacthub-repo.yml
|
||||
rsync hack/artifacthub/artifacthub-repo.yml $LOCAL_OSS_DIRECTORY
|
||||
- name: Package helm charts
|
||||
run: |
|
||||
helm package $HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $MINIMAL_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $LEGACY_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $VELA_ROLLOUT_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm repo index --url https://$BUCKET.$ENDPOINT/core $LOCAL_OSS_DIRECTORY
|
||||
- name: sync local to cloud
|
||||
run: ./ossutil --config-file .ossutilconfig sync $LOCAL_OSS_DIRECTORY oss://$BUCKET/core -f -u
|
||||
166
.github/workflows/registry.yml
vendored
166
.github/workflows/registry.yml
vendored
@@ -8,14 +8,11 @@ on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
env:
|
||||
BUCKET: ${{ secrets.OSS_BUCKET }}
|
||||
ENDPOINT: ${{ secrets.OSS_ENDPOINT }}
|
||||
ACCESS_KEY: ${{ secrets.OSS_ACCESS_KEY }}
|
||||
ACCESS_KEY_SECRET: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
|
||||
ARTIFACT_HUB_REPOSITORY_ID: ${{ secrets.ARTIFACT_HUB_REPOSITORY_ID }}
|
||||
|
||||
jobs:
|
||||
publish-images:
|
||||
publish-core-images:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
@@ -47,20 +44,16 @@ jobs:
|
||||
- name: Login Alibaba Cloud ACR
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: kubevela-registry.cn-hangzhou.cr.aliyuncs.com
|
||||
username: ${{ secrets.ACR_USERNAME }}@aliyun-inner.com
|
||||
registry: ${{ secrets.ACR_DOMAIN }}
|
||||
username: ${{ secrets.ACR_USERNAME }}
|
||||
password: ${{ secrets.ACR_PASSWORD }}
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
driver-opts: image=moby/buildkit:master
|
||||
|
||||
- name: Build & Pushing vela-core for ACR
|
||||
run: |
|
||||
docker build --build-arg GOPROXY=https://proxy.golang.org --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} --build-arg GITVERSION=git-${{ steps.vars.outputs.git_revision }} -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }} .
|
||||
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing vela-core for Dockerhub and GHCR
|
||||
name: Build & Pushing vela-core for Dockerhub, GHCR and ACR
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
@@ -75,14 +68,51 @@ jobs:
|
||||
GOPROXY=https://proxy.golang.org
|
||||
tags: |-
|
||||
docker.io/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository }}/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository_owner }}/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
${{ secrets.ACR_DOMAIN }}/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
- name: Build & Pushing vela-apiserver for ACR
|
||||
publish-addon-images:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
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 }}
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
if [[ ${GITHUB_REF} == "refs/heads/master" ]]; then
|
||||
VERSION=latest
|
||||
fi
|
||||
echo ::set-output name=VERSION::${VERSION}
|
||||
- name: Get git revision
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::set-output name=git_revision::$(git rev-parse --short HEAD)"
|
||||
- name: Login ghcr.io
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login docker.io
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Login Alibaba Cloud ACR
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ${{ secrets.ACR_DOMAIN }}
|
||||
username: ${{ secrets.ACR_USERNAME }}
|
||||
password: ${{ secrets.ACR_PASSWORD }}
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
driver-opts: image=moby/buildkit:master
|
||||
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing vela-apiserver for Dockerhub and GHCR
|
||||
name: Build & Pushing vela-apiserver for Dockerhub, GHCR and ACR
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.apiserver
|
||||
@@ -97,14 +127,10 @@ 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 }}
|
||||
|
||||
- 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 }}
|
||||
ghcr.io/${{ github.repository_owner }}/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
${{ secrets.ACR_DOMAIN }}/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing runtime rollout for Dockerhub and GHCR
|
||||
name: Build & Pushing runtime rollout Dockerhub, GHCR and ACR
|
||||
with:
|
||||
context: .
|
||||
file: runtime/rollout/Dockerfile
|
||||
@@ -119,96 +145,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 }}
|
||||
|
||||
publish-charts:
|
||||
env:
|
||||
HELM_CHARTS_DIR: charts
|
||||
HELM_CHART: charts/vela-core
|
||||
MINIMAL_HELM_CHART: charts/vela-minimal
|
||||
LEGACY_HELM_CHART: legacy/charts/vela-core-legacy
|
||||
OAM_RUNTIME_HELM_CHART: charts/oam-runtime
|
||||
VELA_ROLLOUT_HELM_CHART: runtime/rollout/charts
|
||||
LOCAL_OSS_DIRECTORY: .oss/
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Get git revision
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::set-output name=git_revision::$(git rev-parse --short HEAD)"
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: v3.4.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Generate helm doc
|
||||
run: |
|
||||
make helm-doc-gen
|
||||
- name: Prepare legacy chart
|
||||
run: |
|
||||
rsync -r $LEGACY_HELM_CHART $HELM_CHARTS_DIR
|
||||
rsync -r $HELM_CHART/* $LEGACY_HELM_CHART --exclude=Chart.yaml --exclude=crds
|
||||
- name: Prepare vela chart
|
||||
run: |
|
||||
rsync -r $VELA_ROLLOUT_HELM_CHART $HELM_CHARTS_DIR
|
||||
- uses: oprypin/find-latest-tag@v1
|
||||
with:
|
||||
repository: oam-dev/kubevela
|
||||
releases-only: true
|
||||
id: latest_tag
|
||||
- name: Tag helm chart image
|
||||
run: |
|
||||
latest_repo_tag=${{ steps.latest_tag.outputs.tag }}
|
||||
sub="."
|
||||
major="$(cut -d"$sub" -f1 <<<"$latest_repo_tag")"
|
||||
minor="$(cut -d"$sub" -f2 <<<"$latest_repo_tag")"
|
||||
patch="0"
|
||||
current_repo_tag="$major.$minor.$patch"
|
||||
image_tag=${GITHUB_REF#refs/tags/}
|
||||
chart_version=$latest_repo_tag
|
||||
if [[ ${GITHUB_REF} == "refs/heads/master" ]]; then
|
||||
image_tag=latest
|
||||
chart_version=${current_repo_tag}-nightly-build
|
||||
fi
|
||||
sed -i "s/latest/${image_tag}/g" $HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $MINIMAL_HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $LEGACY_HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $OAM_RUNTIME_HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $VELA_ROLLOUT_HELM_CHART/values.yaml
|
||||
chart_smever=${chart_version#"v"}
|
||||
sed -i "s/0.1.0/$chart_smever/g" $HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $MINIMAL_HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $LEGACY_HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $OAM_RUNTIME_HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $VELA_ROLLOUT_HELM_CHART/Chart.yaml
|
||||
- name: Install ossutil
|
||||
run: wget http://gosspublic.alicdn.com/ossutil/1.7.0/ossutil64 && chmod +x ossutil64 && mv ossutil64 ossutil
|
||||
- name: Configure Alibaba Cloud OSSUTIL
|
||||
run: ./ossutil --config-file .ossutilconfig config -i ${ACCESS_KEY} -k ${ACCESS_KEY_SECRET} -e ${ENDPOINT} -c .ossutilconfig
|
||||
- name: sync cloud to local
|
||||
run: ./ossutil --config-file .ossutilconfig sync oss://$BUCKET/core $LOCAL_OSS_DIRECTORY
|
||||
- name: add artifacthub stuff to the repo
|
||||
run: |
|
||||
rsync $HELM_CHART/README.md $LEGACY_HELM_CHART/README.md
|
||||
rsync $HELM_CHART/README.md $OAM_RUNTIME_HELM_CHART/README.md
|
||||
rsync $HELM_CHART/README.md $VELA_ROLLOUT_HELM_CHART/README.md
|
||||
sed -i "s/ARTIFACT_HUB_REPOSITORY_ID/$ARTIFACT_HUB_REPOSITORY_ID/g" hack/artifacthub/artifacthub-repo.yml
|
||||
rsync hack/artifacthub/artifacthub-repo.yml $LOCAL_OSS_DIRECTORY
|
||||
- name: Package helm charts
|
||||
run: |
|
||||
helm package $HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $MINIMAL_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $LEGACY_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $OAM_RUNTIME_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $VELA_ROLLOUT_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm repo index --url https://$BUCKET.$ENDPOINT/core $LOCAL_OSS_DIRECTORY
|
||||
- name: sync local to cloud
|
||||
run: ./ossutil --config-file .ossutilconfig sync $LOCAL_OSS_DIRECTORY oss://$BUCKET/core -f
|
||||
ghcr.io/${{ github.repository_owner }}/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
|
||||
${{ secrets.ACR_DOMAIN }}/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
publish-capabilities:
|
||||
env:
|
||||
@@ -227,4 +165,4 @@ jobs:
|
||||
- name: rsync all capabilites
|
||||
run: rsync vela-templates/registry/auto-gen/* $CAPABILITY_DIR
|
||||
- name: sync local to cloud
|
||||
run: ./ossutil --config-file .ossutilconfig sync $CAPABILITY_DIR oss://$CAPABILITY_BUCKET -f
|
||||
run: ./ossutil --config-file .ossutilconfig sync $CAPABILITY_DIR oss://$CAPABILITY_BUCKET -f -u
|
||||
11
.github/workflows/release.yml
vendored
11
.github/workflows/release.yml
vendored
@@ -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
@@ -106,7 +106,7 @@ spec:
|
||||
}]
|
||||
}
|
||||
}
|
||||
parameter: #PatchParams | close({
|
||||
parameter: *#PatchParams | close({
|
||||
// +usage=Specify the commands for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -69,7 +69,7 @@ spec:
|
||||
}]
|
||||
}
|
||||
}
|
||||
parameter: #PatchParams | close({
|
||||
parameter: *#PatchParams | close({
|
||||
// +usage=Specify the container image for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -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
@@ -106,7 +106,7 @@ spec:
|
||||
}]
|
||||
}
|
||||
}
|
||||
parameter: #PatchParams | close({
|
||||
parameter: *#PatchParams | close({
|
||||
// +usage=Specify the commands for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -69,7 +69,7 @@ spec:
|
||||
}]
|
||||
}
|
||||
}
|
||||
parameter: #PatchParams | close({
|
||||
parameter: *#PatchParams | close({
|
||||
// +usage=Specify the container image for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -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]
|
||||
})
|
||||
|
||||
@@ -142,6 +142,7 @@ func main() {
|
||||
flag.DurationVar(&clusterMetricsInterval, "cluster-metrics-interval", 15*time.Second, "The interval that ClusterMetricsMgr will collect metrics from clusters, default value is 15 seconds.")
|
||||
flag.BoolVar(&controllerArgs.EnableCompatibility, "enable-asi-compatibility", false, "enable compatibility for asi")
|
||||
flag.BoolVar(&controllerArgs.IgnoreAppWithoutControllerRequirement, "ignore-app-without-controller-version", false, "If true, application controller will not process the app without 'app.oam.dev/controller-version-require' annotation")
|
||||
flag.BoolVar(&controllerArgs.IgnoreDefinitionWithoutControllerRequirement, "ignore-definition-without-controller-version", false, "If true, trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation")
|
||||
standardcontroller.AddOptimizeFlags()
|
||||
standardcontroller.AddAdmissionFlags()
|
||||
flag.IntVar(&resourcekeeper.MaxDispatchConcurrent, "max-dispatch-concurrent", 10, "Set the max dispatch concurrent number, default is 10")
|
||||
|
||||
@@ -56,7 +56,7 @@ else
|
||||
CUE=$(shell which cue)
|
||||
endif
|
||||
|
||||
KUSTOMIZE_VERSION ?= 3.8.2
|
||||
KUSTOMIZE_VERSION ?= 4.5.4
|
||||
|
||||
.PHONY: kustomize
|
||||
kustomize:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
26
pkg/appfile/testdata/backport-1-2/app.yaml
vendored
Normal file
26
pkg/appfile/testdata/backport-1-2/app.yaml
vendored
Normal 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
|
||||
271
pkg/appfile/testdata/backport-1-2/apprev1.yaml
vendored
Normal file
271
pkg/appfile/testdata/backport-1-2/apprev1.yaml
vendored
Normal 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: {}
|
||||
277
pkg/appfile/testdata/backport-1-2/apprev2.yaml
vendored
Normal file
277
pkg/appfile/testdata/backport-1-2/apprev2.yaml
vendored
Normal 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: {}
|
||||
271
pkg/appfile/testdata/backport-1-2/apprev3.yaml
vendored
Normal file
271
pkg/appfile/testdata/backport-1-2/apprev3.yaml
vendored
Normal 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: {}
|
||||
19
pkg/appfile/testdata/backport-1-2/wsd.yaml
vendored
Normal file
19
pkg/appfile/testdata/backport-1-2/wsd.yaml
vendored
Normal 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 & {}
|
||||
@@ -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,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -86,4 +86,7 @@ type Args struct {
|
||||
|
||||
// IgnoreAppWithoutControllerRequirement indicates that application controller will not process the app without 'app.oam.dev/controller-version-require' annotation.
|
||||
IgnoreAppWithoutControllerRequirement bool
|
||||
|
||||
// IgnoreDefinitionWithoutControllerRequirement indicates that trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation.
|
||||
IgnoreDefinitionWithoutControllerRequirement bool
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
if annotations := app.GetAnnotations(); annotations == nil || annotations[oam.AnnotationKubeVelaVersion] == "" {
|
||||
metav1.SetMetaDataAnnotation(&app.ObjectMeta, oam.AnnotationKubeVelaVersion, version.VelaVersion)
|
||||
}
|
||||
logCtx.AddTag("publish_version", app.GetAnnotations()[oam.AnnotationKubeVelaVersion])
|
||||
logCtx.AddTag("publish_version", app.GetAnnotations()[oam.AnnotationPublishVersion])
|
||||
|
||||
appParser := appfile.NewApplicationParser(r.Client, r.dm, r.pd)
|
||||
handler, err := NewAppHandler(logCtx, r, app, appParser)
|
||||
|
||||
@@ -3897,7 +3897,7 @@ spec:
|
||||
}]
|
||||
}
|
||||
}
|
||||
parameter: #PatchParams | close({
|
||||
parameter: *#PatchParams | close({
|
||||
// +usage=Specify the environment variables for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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: []
|
||||
@@ -43,17 +43,24 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// Reconciler reconciles a ComponentDefinition object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
defRevLimit int
|
||||
concurrentReconciles int
|
||||
ignoreDefNoCtrlReq bool
|
||||
controllerVersion string
|
||||
}
|
||||
|
||||
// Reconcile is the main logic for ComponentDefinition controller
|
||||
@@ -68,6 +75,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !r.matchControllerRequirement(&componentDefinition) {
|
||||
klog.InfoS("skip componentDefinition: not match the controller requirement of componentDefinition", "componentDefinition", klog.KObj(&componentDefinition))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// refresh package discover when componentDefinition is registered
|
||||
if componentDefinition.Spec.Workload.Type != types.AutoDetectWorkloadDefinition {
|
||||
err := utils.RefreshPackageDiscover(ctx, r.Client, r.dm, r.pd, &componentDefinition)
|
||||
@@ -187,12 +199,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Setup adds a controller that reconciles ComponentDefinition.
|
||||
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
|
||||
r := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
options: parseOptions(args),
|
||||
}
|
||||
return r.SetupWithManager(mgr)
|
||||
}
|
||||
|
||||
func parseOptions(args oamctrl.Args) options {
|
||||
return options{
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
|
||||
controllerVersion: version.VelaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) matchControllerRequirement(componentDefinition *v1beta1.ComponentDefinition) bool {
|
||||
if componentDefinition.Annotations != nil {
|
||||
if requireVersion, ok := componentDefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
|
||||
return requireVersion == r.controllerVersion
|
||||
}
|
||||
}
|
||||
if r.ignoreDefNoCtrlReq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r = Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
defRevLimit: defRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
options: options{
|
||||
defRevLimit: defRevisionLimit,
|
||||
},
|
||||
}
|
||||
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
|
||||
var ctx context.Context
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -42,17 +42,24 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// Reconciler reconciles a TraitDefinition object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
defRevLimit int
|
||||
concurrentReconciles int
|
||||
ignoreDefNoCtrlReq bool
|
||||
controllerVersion string
|
||||
}
|
||||
|
||||
// Reconcile is the main logic for TraitDefinition controller
|
||||
@@ -67,6 +74,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !r.matchControllerRequirement(&traitdefinition) {
|
||||
klog.InfoS("skip traitDefinition: not match the controller requirement of traitDefinition", "traitDefinition", klog.KObj(&traitdefinition))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// this is a placeholder for finalizer here in the future
|
||||
if traitdefinition.DeletionTimestamp != nil {
|
||||
klog.InfoS("The TraitDefinition is being deleted", "traitDefinition", klog.KRef(req.Namespace, req.Name))
|
||||
@@ -193,12 +205,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Setup adds a controller that reconciles TraitDefinition.
|
||||
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
|
||||
r := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
options: parseOptions(args),
|
||||
}
|
||||
return r.SetupWithManager(mgr)
|
||||
}
|
||||
|
||||
func parseOptions(args oamctrl.Args) options {
|
||||
return options{
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
|
||||
controllerVersion: version.VelaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) matchControllerRequirement(traitDefinition *v1beta1.TraitDefinition) bool {
|
||||
if traitDefinition.Annotations != nil {
|
||||
if requireVersion, ok := traitDefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
|
||||
return requireVersion == r.controllerVersion
|
||||
}
|
||||
}
|
||||
if r.ignoreDefNoCtrlReq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r = Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
defRevLimit: defRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
options: options{
|
||||
defRevLimit: defRevisionLimit,
|
||||
},
|
||||
}
|
||||
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
|
||||
var ctx context.Context
|
||||
|
||||
@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r = Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
defRevLimit: defRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
options: options{
|
||||
defRevLimit: defRevisionLimit,
|
||||
},
|
||||
}
|
||||
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
|
||||
var ctx context.Context
|
||||
|
||||
@@ -42,17 +42,24 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// Reconciler reconciles a WorkflowStepDefinition object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
defRevLimit int
|
||||
concurrentReconciles int
|
||||
ignoreDefNoCtrlReq bool
|
||||
controllerVersion string
|
||||
}
|
||||
|
||||
// Reconcile is the main logic for WorkflowStepDefinition controller
|
||||
@@ -68,6 +75,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !r.matchControllerRequirement(&wfstepdefinition) {
|
||||
klog.InfoS("skip workflowStepDefinition: not match the controller requirement of workflowStepDefinition", "workflowStepDefinition", klog.KObj(&wfstepdefinition))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// this is a placeholder for finalizer here in the future
|
||||
if wfstepdefinition.DeletionTimestamp != nil {
|
||||
return ctrl.Result{}, nil
|
||||
@@ -192,11 +204,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Setup adds a controller that reconciles WorkflowStepDefinition.
|
||||
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
|
||||
r := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
options: parseOptions(args),
|
||||
}
|
||||
return r.SetupWithManager(mgr)
|
||||
}
|
||||
|
||||
func parseOptions(args oamctrl.Args) options {
|
||||
return options{
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
|
||||
controllerVersion: version.VelaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) matchControllerRequirement(wfstepdefinition *v1beta1.WorkflowStepDefinition) bool {
|
||||
if wfstepdefinition.Annotations != nil {
|
||||
if requireVersion, ok := wfstepdefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
|
||||
return requireVersion == r.controllerVersion
|
||||
}
|
||||
}
|
||||
if r.ignoreDefNoCtrlReq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -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
|
||||
@@ -196,7 +196,7 @@ const (
|
||||
// AnnotationWorkloadName indicates the managed workload's name by trait
|
||||
AnnotationWorkloadName = "trait.oam.dev/workload-name"
|
||||
|
||||
// AnnotationControllerRequirement indicates the controller version that can process the application.
|
||||
// AnnotationControllerRequirement indicates the controller version that can process the application/definition.
|
||||
AnnotationControllerRequirement = "app.oam.dev/controller-version-require"
|
||||
|
||||
// AnnotationServiceAccountName indicates the name of the ServiceAccount to use to apply Components and run Workflow.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -126,7 +126,7 @@ func (h *provider) ApplyInParallel(ctx wfContext.Context, v *value.Value, act ty
|
||||
deployCtx := multicluster.ContextWithClusterName(context.Background(), cluster)
|
||||
deployCtx = h.setServiceAccountInContext(deployCtx)
|
||||
if err = h.apply(deployCtx, cluster, common.WorkflowResourceCreator, workloads...); err != nil {
|
||||
return v.FillObject(err, "err")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
125
references/cli/addon_suite_test.go
Normal file
125
references/cli/addon_suite_test.go
Normal 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"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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: {}`,
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}]
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 != "" {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -100,7 +100,7 @@ template: {
|
||||
}
|
||||
}
|
||||
|
||||
parameter: #PatchParams | close({
|
||||
parameter: *#PatchParams | close({
|
||||
// +usage=Specify the commands for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -65,7 +65,7 @@ template: {
|
||||
}
|
||||
}
|
||||
|
||||
parameter: #PatchParams | close({
|
||||
parameter: *#PatchParams | close({
|
||||
// +usage=Specify the container image for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
|
||||
@@ -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]
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user