mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-23 22:33:58 +00:00
Compare commits
46 Commits
v1.4.1
...
v1.4.7-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b596b70ebe | ||
|
|
0cd370e867 | ||
|
|
d9adc73e5c | ||
|
|
4a2d9807c8 | ||
|
|
840cb8ce58 | ||
|
|
5a64fec916 | ||
|
|
657a374ded | ||
|
|
dfe12cd9ca | ||
|
|
cd42f67848 | ||
|
|
61d2c588e3 | ||
|
|
b3dad698a5 | ||
|
|
ec5159c2ca | ||
|
|
a7b2b221e0 | ||
|
|
caa495a5d9 | ||
|
|
15bea4fb64 | ||
|
|
1a094a4eea | ||
|
|
65b6f47330 | ||
|
|
3a4cd2dca6 | ||
|
|
9fabd950e5 | ||
|
|
0a012b4d34 | ||
|
|
7c231e6c48 | ||
|
|
36b6c3e7b5 | ||
|
|
4cc019722c | ||
|
|
b040ae65da | ||
|
|
f0fb4ed099 | ||
|
|
7f89d12059 | ||
|
|
3c61bcb8f0 | ||
|
|
a14b536fd1 | ||
|
|
ba5a726854 | ||
|
|
ffb9d06427 | ||
|
|
819dc26ace | ||
|
|
5e3ab732df | ||
|
|
62d5507499 | ||
|
|
c0daf688a6 | ||
|
|
48d19a2427 | ||
|
|
4da8d49e60 | ||
|
|
4db9e89816 | ||
|
|
667053409d | ||
|
|
eb9ddaabd3 | ||
|
|
f11a94612f | ||
|
|
56f9d7cb9c | ||
|
|
fbbc666019 | ||
|
|
d0788254cb | ||
|
|
c72a6aef87 | ||
|
|
195b7fe0c7 | ||
|
|
33c9e3b170 |
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
|
||||
198
.github/workflows/registry.yml
vendored
198
.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,36 +68,11 @@ jobs:
|
||||
GOPROXY=https://proxy.golang.org
|
||||
tags: |-
|
||||
docker.io/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository }}/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository_owner }}/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
${{ secrets.ACR_DOMAIN }}/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
- name: Build & Pushing vela-apiserver for ACR
|
||||
run: |
|
||||
docker build --build-arg GOPROXY=https://proxy.golang.org --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} --build-arg GITVERSION=git-${{ steps.vars.outputs.git_revision }} -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }} -f Dockerfile.apiserver .
|
||||
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing vela-apiserver for Dockerhub and GHCR
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.apiserver
|
||||
labels: |-
|
||||
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
build-args: |
|
||||
GITVERSION=git-${{ steps.vars.outputs.git_revision }}
|
||||
VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||
GOPROXY=https://proxy.golang.org
|
||||
tags: |-
|
||||
docker.io/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository }}/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
- name: Build & Pushing vela CLI for ACR
|
||||
run: |
|
||||
docker build --build-arg GOPROXY=https://proxy.golang.org --build-arg VERSION=${{ steps.get_version.outputs.VERSION }} --build-arg GITVERSION=git-${{ steps.vars.outputs.git_revision }} -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }} -f Dockerfile.cli .
|
||||
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing CLI for Dockerhub and GHCR
|
||||
name: Build & Pushing CLI for Dockerhub, GHCR and ACR
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.cli
|
||||
@@ -119,10 +87,70 @@ jobs:
|
||||
GOPROXY=https://proxy.golang.org
|
||||
tags: |-
|
||||
docker.io/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository }}/vela-cli:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository_owner }}/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
|
||||
${{ secrets.ACR_DOMAIN }}/oamdev/vela-cli:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
publish-addon-images:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
if [[ ${GITHUB_REF} == "refs/heads/master" ]]; then
|
||||
VERSION=latest
|
||||
fi
|
||||
echo ::set-output name=VERSION::${VERSION}
|
||||
- name: Get git revision
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::set-output name=git_revision::$(git rev-parse --short HEAD)"
|
||||
- name: Login ghcr.io
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login docker.io
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Login Alibaba Cloud ACR
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ${{ secrets.ACR_DOMAIN }}
|
||||
username: ${{ secrets.ACR_USERNAME }}
|
||||
password: ${{ secrets.ACR_PASSWORD }}
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
driver-opts: image=moby/buildkit:master
|
||||
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing runtime rollout for Dockerhub and GHCR
|
||||
name: Build & Pushing vela-apiserver for Dockerhub, GHCR and ACR
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.apiserver
|
||||
labels: |-
|
||||
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
build-args: |
|
||||
GITVERSION=git-${{ steps.vars.outputs.git_revision }}
|
||||
VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||
GOPROXY=https://proxy.golang.org
|
||||
tags: |-
|
||||
docker.io/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository_owner }}/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
${{ secrets.ACR_DOMAIN }}/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing runtime rollout Dockerhub, GHCR and ACR
|
||||
with:
|
||||
context: .
|
||||
file: runtime/rollout/Dockerfile
|
||||
@@ -137,92 +165,8 @@ jobs:
|
||||
GOPROXY=https://proxy.golang.org
|
||||
tags: |-
|
||||
docker.io/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository }}/vela-rollout:${{ steps.get_version.outputs.VERSION }}
|
||||
kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
publish-charts:
|
||||
env:
|
||||
HELM_CHARTS_DIR: charts
|
||||
HELM_CHART: charts/vela-core
|
||||
MINIMAL_HELM_CHART: charts/vela-minimal
|
||||
LEGACY_HELM_CHART: legacy/charts/vela-core-legacy
|
||||
VELA_ROLLOUT_HELM_CHART: runtime/rollout/charts
|
||||
LOCAL_OSS_DIRECTORY: .oss/
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Get git revision
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::set-output name=git_revision::$(git rev-parse --short HEAD)"
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: v3.4.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Generate helm doc
|
||||
run: |
|
||||
make helm-doc-gen
|
||||
- name: Prepare legacy chart
|
||||
run: |
|
||||
rsync -r $LEGACY_HELM_CHART $HELM_CHARTS_DIR
|
||||
rsync -r $HELM_CHART/* $LEGACY_HELM_CHART --exclude=Chart.yaml --exclude=crds
|
||||
- name: Prepare vela chart
|
||||
run: |
|
||||
rsync -r $VELA_ROLLOUT_HELM_CHART $HELM_CHARTS_DIR
|
||||
- uses: oprypin/find-latest-tag@v1
|
||||
with:
|
||||
repository: oam-dev/kubevela
|
||||
releases-only: true
|
||||
id: latest_tag
|
||||
- name: Tag helm chart image
|
||||
run: |
|
||||
latest_repo_tag=${{ steps.latest_tag.outputs.tag }}
|
||||
sub="."
|
||||
major="$(cut -d"$sub" -f1 <<<"$latest_repo_tag")"
|
||||
minor="$(cut -d"$sub" -f2 <<<"$latest_repo_tag")"
|
||||
patch="0"
|
||||
current_repo_tag="$major.$minor.$patch"
|
||||
image_tag=${GITHUB_REF#refs/tags/}
|
||||
chart_version=$latest_repo_tag
|
||||
if [[ ${GITHUB_REF} == "refs/heads/master" ]]; then
|
||||
image_tag=latest
|
||||
chart_version=${current_repo_tag}-nightly-build
|
||||
fi
|
||||
sed -i "s/latest/${image_tag}/g" $HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $MINIMAL_HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $LEGACY_HELM_CHART/values.yaml
|
||||
sed -i "s/latest/${image_tag}/g" $VELA_ROLLOUT_HELM_CHART/values.yaml
|
||||
chart_smever=${chart_version#"v"}
|
||||
sed -i "s/0.1.0/$chart_smever/g" $HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $MINIMAL_HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $LEGACY_HELM_CHART/Chart.yaml
|
||||
sed -i "s/0.1.0/$chart_smever/g" $VELA_ROLLOUT_HELM_CHART/Chart.yaml
|
||||
- name: Install ossutil
|
||||
run: wget http://gosspublic.alicdn.com/ossutil/1.7.0/ossutil64 && chmod +x ossutil64 && mv ossutil64 ossutil
|
||||
- name: Configure Alibaba Cloud OSSUTIL
|
||||
run: ./ossutil --config-file .ossutilconfig config -i ${ACCESS_KEY} -k ${ACCESS_KEY_SECRET} -e ${ENDPOINT} -c .ossutilconfig
|
||||
- name: sync cloud to local
|
||||
run: ./ossutil --config-file .ossutilconfig sync oss://$BUCKET/core $LOCAL_OSS_DIRECTORY
|
||||
- name: add artifacthub stuff to the repo
|
||||
run: |
|
||||
rsync $HELM_CHART/README.md $LEGACY_HELM_CHART/README.md
|
||||
rsync $HELM_CHART/README.md $VELA_ROLLOUT_HELM_CHART/README.md
|
||||
sed -i "s/ARTIFACT_HUB_REPOSITORY_ID/$ARTIFACT_HUB_REPOSITORY_ID/g" hack/artifacthub/artifacthub-repo.yml
|
||||
rsync hack/artifacthub/artifacthub-repo.yml $LOCAL_OSS_DIRECTORY
|
||||
- name: Package helm charts
|
||||
run: |
|
||||
helm package $HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $MINIMAL_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $LEGACY_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm package $VELA_ROLLOUT_HELM_CHART --destination $LOCAL_OSS_DIRECTORY
|
||||
helm repo index --url https://$BUCKET.$ENDPOINT/core $LOCAL_OSS_DIRECTORY
|
||||
- name: sync local to cloud
|
||||
run: ./ossutil --config-file .ossutilconfig sync $LOCAL_OSS_DIRECTORY oss://$BUCKET/core -f
|
||||
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:
|
||||
|
||||
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@@ -123,6 +123,11 @@ jobs:
|
||||
- name: sync the latest version file
|
||||
if: ${{ !contains(env.VELA_VERSION,'alpha') && !contains(env.VELA_VERSION,'beta') }}
|
||||
run: |
|
||||
LATEST_VERSION=$(curl -fsSl https://static.kubevela.net/binary/vela/latest_version)
|
||||
verlte() {
|
||||
[ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ]
|
||||
}
|
||||
verlte ${{ env.VELA_VERSION }} $LATEST_VERSION && echo "${{ env.VELA_VERSION }} <= $LATEST_VERSION, skip update" && exit 0
|
||||
echo ${{ env.VELA_VERSION }} > ./latest_version
|
||||
./ossutil --config-file .ossutilconfig cp -u ./latest_version oss://$BUCKET/binary/vela/latest_version
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ metadata:
|
||||
name: v1alpha1.cluster.core.oam.dev
|
||||
annotations:
|
||||
{{- if and .Values.multicluster.clusterGateway.secureTLS.enabled .Values.multicluster.clusterGateway.secureTLS.certManager.enabled }}
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kubevela.fullname" . }}-cluster-gateway-tls"
|
||||
cert-manager.io/inject-ca-from: "{{ .Release.Namespace }}/{{ template "kubevela.fullname" . }}-cluster-gateway-tls-v2"
|
||||
{{- end }}
|
||||
labels:
|
||||
api: cluster-extension-apiserver
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: affinity specify affinity and tolerationon K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
definition.oam.dev/description: Affinity specifies affinity and toleration K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: affinity
|
||||
|
||||
@@ -20,6 +20,7 @@ spec:
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
output: {
|
||||
@@ -42,21 +43,29 @@ spec:
|
||||
if parameter.auth == _|_ {
|
||||
type: "Opaque"
|
||||
}
|
||||
if parameter.auth != _|_ {
|
||||
stringData: ".dockerconfigjson": json.Marshal({
|
||||
auths: "\(parameter.registry)": {
|
||||
username: parameter.auth.username
|
||||
password: parameter.auth.password
|
||||
if parameter.auth.email != _|_ {
|
||||
email: parameter.auth.email
|
||||
stringData: {
|
||||
if parameter.auth != _|_ && parameter.auth.username != _|_ {
|
||||
".dockerconfigjson": json.Marshal({
|
||||
auths: "\(parameter.registry)": {
|
||||
username: parameter.auth.username
|
||||
password: parameter.auth.password
|
||||
if parameter.auth.email != _|_ {
|
||||
email: parameter.auth.email
|
||||
}
|
||||
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
|
||||
}
|
||||
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if parameter.insecure != _|_ {
|
||||
"insecure-skip-verify": strconv.FormatBool(parameter.insecure)
|
||||
}
|
||||
if parameter.useHTTP != _|_ {
|
||||
"protocol-use-http": strconv.FormatBool(parameter.useHTTP)
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Image registry FQDN
|
||||
// +usage=Image registry FQDN, such as: index.docker.io
|
||||
registry: string
|
||||
// +usage=Authenticate the image registry
|
||||
auth?: {
|
||||
@@ -67,6 +76,10 @@ spec:
|
||||
// +usage=Private Image registry email
|
||||
email?: string
|
||||
}
|
||||
// +usage=For the registry server that uses the self-signed certificate
|
||||
insecure?: bool
|
||||
// +usage=For the registry server that uses the HTTP protocol
|
||||
useHTTP?: bool
|
||||
}
|
||||
workload:
|
||||
type: autodetects.core.oam.dev
|
||||
|
||||
@@ -196,14 +196,14 @@ spec:
|
||||
// +usage=Specifies a source the value of this var should come from
|
||||
valueFrom?: {
|
||||
// +usage=Selects a key of a secret in the pod's namespace
|
||||
secretKeyRef: {
|
||||
secretKeyRef?: {
|
||||
// +usage=The name of the secret in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the secret to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
// +usage=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef: {
|
||||
configMapKeyRef?: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
|
||||
@@ -43,7 +43,7 @@ spec:
|
||||
volumeMounts: [{
|
||||
name: parameter.mountName
|
||||
mountPath: parameter.initMountPath
|
||||
}]
|
||||
}] + parameter.extraVolumeMounts
|
||||
}]
|
||||
// +patchKey=name
|
||||
volumes: [{
|
||||
@@ -97,5 +97,13 @@ spec:
|
||||
|
||||
// +usage=Specify the mount path of init container
|
||||
initMountPath: string
|
||||
|
||||
// +usage=Specify the extra volume mounts for the init container
|
||||
extraVolumeMounts: [...{
|
||||
// +usage=The name of the volume to be mounted
|
||||
name: string
|
||||
// +usage=The mountPath for mount in the init container
|
||||
mountPath: string
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,18 @@ spec:
|
||||
cue:
|
||||
template: |
|
||||
#K8sObject: {
|
||||
apiVersion: string
|
||||
kind: string
|
||||
metadata: {
|
||||
name: string
|
||||
...
|
||||
}
|
||||
// +usage=The resource type for the Kubernetes objects
|
||||
resource?: string
|
||||
// +usage=The group name for the Kubernetes objects
|
||||
group?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects with the name, exclusive to labelSelector
|
||||
name?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects from the namespace. Otherwise, fetch from the application's namespace.
|
||||
namespace?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects from the cluster. Otherwise, fetch from the local cluster.
|
||||
cluster?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects according to the label selector, exclusive to name
|
||||
labelSelector?: [string]: string
|
||||
...
|
||||
}
|
||||
output: parameter.objects[0]
|
||||
@@ -30,7 +36,12 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: objects: [...#K8sObject]
|
||||
parameter: {
|
||||
// +usage=If specified, application will fetch native Kubernetes objects according to the object description
|
||||
objects?: [...#K8sObject]
|
||||
// +usage=If specified, the objects in the urls will be loaded.
|
||||
urls?: [...string]
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
if context.output.apiVersion == "apps/v1" && context.output.kind == "Deployment" {
|
||||
|
||||
@@ -14,10 +14,114 @@ spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
#Privileges: {
|
||||
// +usage=Specify the verbs to be allowed for the resource
|
||||
verbs: [...string]
|
||||
// +usage=Specify the apiGroups of the resource
|
||||
apiGroups?: [...string]
|
||||
// +usage=Specify the resources to be allowed
|
||||
resources?: [...string]
|
||||
// +usage=Specify the resourceNames to be allowed
|
||||
resourceNames?: [...string]
|
||||
// +usage=Specify the resource url to be allowed
|
||||
nonResourceURLs?: [...string]
|
||||
// +usage=Specify the scope of the privileges, default to be namespace scope
|
||||
scope: *"namespace" | "cluster"
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the name of ServiceAccount
|
||||
name: string
|
||||
// +usage=Specify whether to create new ServiceAccount or not
|
||||
create: *false | bool
|
||||
// +usage=Specify the privileges of the ServiceAccount, if not empty, RoleBindings(ClusterRoleBindings) will be created
|
||||
privileges?: [...#Privileges]
|
||||
}
|
||||
// +patchStrategy=retainKeys
|
||||
patch: spec: template: spec: serviceAccountName: parameter.name
|
||||
_clusterPrivileges: [ for p in parameter.privileges if p.scope == "cluster" {p}]
|
||||
_namespacePrivileges: [ for p in parameter.privileges if p.scope == "namespace" {p}]
|
||||
outputs: {
|
||||
if parameter.create {
|
||||
"service-account": {
|
||||
apiVersion: "v1"
|
||||
kind: "ServiceAccount"
|
||||
metadata: name: parameter.name
|
||||
}
|
||||
}
|
||||
if parameter.privileges != _|_ {
|
||||
if len(_clusterPrivileges) > 0 {
|
||||
"cluster-role": {
|
||||
apiVersion: "rbac.authorization.k8s.io/v1"
|
||||
kind: "ClusterRole"
|
||||
metadata: name: "\(context.namespace):\(parameter.name)"
|
||||
rules: [ for p in _clusterPrivileges {
|
||||
verbs: p.verbs
|
||||
if p.apiGroups != _|_ {
|
||||
apiGroups: p.apiGroups
|
||||
}
|
||||
if p.resources != _|_ {
|
||||
resources: p.resources
|
||||
}
|
||||
if p.resourceNames != _|_ {
|
||||
resourceNames: p.resourceNames
|
||||
}
|
||||
if p.nonResourceURLs != _|_ {
|
||||
nonResourceURLs: p.nonResourceURLs
|
||||
}
|
||||
}]
|
||||
}
|
||||
"cluster-role-binding": {
|
||||
apiVersion: "rbac.authorization.k8s.io/v1"
|
||||
kind: "ClusterRoleBinding"
|
||||
metadata: name: "\(context.namespace):\(parameter.name)"
|
||||
roleRef: {
|
||||
apiGroup: "rbac.authorization.k8s.io"
|
||||
kind: "ClusterRole"
|
||||
name: "\(context.namespace):\(parameter.name)"
|
||||
}
|
||||
subjects: [{
|
||||
kind: "ServiceAccount"
|
||||
name: parameter.name
|
||||
namespace: "\(context.namespace)"
|
||||
}]
|
||||
}
|
||||
}
|
||||
if len(_namespacePrivileges) > 0 {
|
||||
role: {
|
||||
apiVersion: "rbac.authorization.k8s.io/v1"
|
||||
kind: "Role"
|
||||
metadata: name: parameter.name
|
||||
rules: [ for p in _namespacePrivileges {
|
||||
verbs: p.verbs
|
||||
if p.apiGroups != _|_ {
|
||||
apiGroups: p.apiGroups
|
||||
}
|
||||
if p.resources != _|_ {
|
||||
resources: p.resources
|
||||
}
|
||||
if p.resourceNames != _|_ {
|
||||
resourceNames: p.resourceNames
|
||||
}
|
||||
if p.nonResourceURLs != _|_ {
|
||||
nonResourceURLs: p.nonResourceURLs
|
||||
}
|
||||
}]
|
||||
}
|
||||
"role-binding": {
|
||||
apiVersion: "rbac.authorization.k8s.io/v1"
|
||||
kind: "RoleBinding"
|
||||
metadata: name: parameter.name
|
||||
roleRef: {
|
||||
apiGroup: "rbac.authorization.k8s.io"
|
||||
kind: "Role"
|
||||
name: parameter.name
|
||||
}
|
||||
subjects: [{
|
||||
kind: "ServiceAccount"
|
||||
name: parameter.name
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,11 @@ spec:
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
// +usage=Specify the field reference for env
|
||||
fieldRef?: {
|
||||
// +usage=Specify the field path for env
|
||||
fieldPath: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ spec:
|
||||
{
|
||||
name: "pvc-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -73,6 +76,9 @@ spec:
|
||||
{
|
||||
name: "configmap-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -103,6 +109,9 @@ spec:
|
||||
{
|
||||
name: "secret-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -133,6 +142,9 @@ spec:
|
||||
{
|
||||
name: "emptydir-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -141,12 +153,28 @@ spec:
|
||||
{
|
||||
name: "pvc-" + v.name
|
||||
devicePath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
volumesList: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
|
||||
deDupVolumesArray: [
|
||||
for val in [
|
||||
for i, vi in volumesList {
|
||||
for j, vj in volumesList if j < i && vi.name == vj.name {
|
||||
_ignore: true
|
||||
}
|
||||
vi
|
||||
},
|
||||
] if val._ignore == _|_ {
|
||||
val
|
||||
},
|
||||
]
|
||||
patch: spec: template: spec: {
|
||||
// +patchKey=name
|
||||
volumes: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
|
||||
volumes: deDupVolumesArray
|
||||
|
||||
containers: [{
|
||||
// +patchKey=name
|
||||
@@ -234,6 +262,7 @@ spec:
|
||||
name: string
|
||||
mountOnly: *false | bool
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
volumeMode: *"Filesystem" | string
|
||||
volumeName?: string
|
||||
accessModes: *["ReadWriteOnce"] | [...string]
|
||||
@@ -275,6 +304,7 @@ spec:
|
||||
configMapKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
data?: {...}
|
||||
@@ -298,6 +328,7 @@ spec:
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
stringData?: {...}
|
||||
@@ -313,6 +344,7 @@ spec:
|
||||
emptyDir?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -149,14 +149,14 @@ spec:
|
||||
// +usage=Specifies a source the value of this var should come from
|
||||
valueFrom?: {
|
||||
// +usage=Selects a key of a secret in the pod's namespace
|
||||
secretKeyRef: {
|
||||
secretKeyRef?: {
|
||||
// +usage=The name of the secret in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the secret to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
// +usage=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef: {
|
||||
configMapKeyRef?: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
|
||||
@@ -20,7 +20,10 @@ spec:
|
||||
for v in parameter.volumeMounts.pvc {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -29,7 +32,10 @@ spec:
|
||||
for v in parameter.volumeMounts.configMap {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -38,7 +44,10 @@ spec:
|
||||
for v in parameter.volumeMounts.secret {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -47,7 +56,10 @@ spec:
|
||||
for v in parameter.volumeMounts.emptyDir {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -56,7 +68,10 @@ spec:
|
||||
for v in parameter.volumeMounts.hostPath {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -119,6 +134,19 @@ spec:
|
||||
},
|
||||
] | []
|
||||
}
|
||||
volumesList: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
|
||||
deDupVolumesArray: [
|
||||
for val in [
|
||||
for i, vi in volumesList {
|
||||
for j, vj in volumesList if j < i && vi.name == vj.name {
|
||||
_ignore: true
|
||||
}
|
||||
vi
|
||||
},
|
||||
] if val._ignore == _|_ {
|
||||
val
|
||||
},
|
||||
]
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
@@ -262,7 +290,7 @@ spec:
|
||||
}
|
||||
|
||||
if parameter["volumeMounts"] != _|_ {
|
||||
volumes: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
|
||||
volumes: deDupVolumesArray
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -375,6 +403,7 @@ spec:
|
||||
pvc?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
// +usage=The name of the PVC
|
||||
claimName: string
|
||||
}]
|
||||
@@ -382,6 +411,7 @@ spec:
|
||||
configMap?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
cmName: string
|
||||
items?: [...{
|
||||
@@ -394,6 +424,7 @@ spec:
|
||||
secret?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
secretName: string
|
||||
items?: [...{
|
||||
@@ -406,12 +437,14 @@ spec:
|
||||
emptyDir?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
// +usage=Mount HostPath type volume
|
||||
hostPath?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
path: string
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -122,6 +122,7 @@ metadata:
|
||||
name: {{ include "kubevela.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
controller.oam.dev/name: vela-core
|
||||
{{- include "kubevela.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
|
||||
@@ -226,7 +226,7 @@ admissionWebhooks:
|
||||
enabled: true
|
||||
image:
|
||||
repository: oamdev/kube-webhook-certgen
|
||||
tag: v2.4.0
|
||||
tag: v2.4.1
|
||||
pullPolicy: IfNotPresent
|
||||
nodeSelector: {}
|
||||
affinity: {}
|
||||
|
||||
@@ -4,7 +4,7 @@ apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: affinity specify affinity and tolerationon K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
definition.oam.dev/description: Affinity specifies affinity and toleration K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: affinity
|
||||
|
||||
@@ -20,6 +20,7 @@ spec:
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
output: {
|
||||
@@ -42,21 +43,29 @@ spec:
|
||||
if parameter.auth == _|_ {
|
||||
type: "Opaque"
|
||||
}
|
||||
if parameter.auth != _|_ {
|
||||
stringData: ".dockerconfigjson": json.Marshal({
|
||||
auths: "\(parameter.registry)": {
|
||||
username: parameter.auth.username
|
||||
password: parameter.auth.password
|
||||
if parameter.auth.email != _|_ {
|
||||
email: parameter.auth.email
|
||||
stringData: {
|
||||
if parameter.auth != _|_ && parameter.auth.username != _|_ {
|
||||
".dockerconfigjson": json.Marshal({
|
||||
auths: "\(parameter.registry)": {
|
||||
username: parameter.auth.username
|
||||
password: parameter.auth.password
|
||||
if parameter.auth.email != _|_ {
|
||||
email: parameter.auth.email
|
||||
}
|
||||
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
|
||||
}
|
||||
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if parameter.insecure != _|_ {
|
||||
"insecure-skip-verify": strconv.FormatBool(parameter.insecure)
|
||||
}
|
||||
if parameter.useHTTP != _|_ {
|
||||
"protocol-use-http": strconv.FormatBool(parameter.useHTTP)
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Image registry FQDN
|
||||
// +usage=Image registry FQDN, such as: index.docker.io
|
||||
registry: string
|
||||
// +usage=Authenticate the image registry
|
||||
auth?: {
|
||||
@@ -67,6 +76,10 @@ spec:
|
||||
// +usage=Private Image registry email
|
||||
email?: string
|
||||
}
|
||||
// +usage=For the registry server that uses the self-signed certificate
|
||||
insecure?: bool
|
||||
// +usage=For the registry server that uses the HTTP protocol
|
||||
useHTTP?: bool
|
||||
}
|
||||
workload:
|
||||
type: autodetects.core.oam.dev
|
||||
|
||||
@@ -196,14 +196,14 @@ spec:
|
||||
// +usage=Specifies a source the value of this var should come from
|
||||
valueFrom?: {
|
||||
// +usage=Selects a key of a secret in the pod's namespace
|
||||
secretKeyRef: {
|
||||
secretKeyRef?: {
|
||||
// +usage=The name of the secret in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the secret to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
// +usage=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef: {
|
||||
configMapKeyRef?: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
|
||||
@@ -43,7 +43,7 @@ spec:
|
||||
volumeMounts: [{
|
||||
name: parameter.mountName
|
||||
mountPath: parameter.initMountPath
|
||||
}]
|
||||
}] + parameter.extraVolumeMounts
|
||||
}]
|
||||
// +patchKey=name
|
||||
volumes: [{
|
||||
@@ -97,5 +97,13 @@ spec:
|
||||
|
||||
// +usage=Specify the mount path of init container
|
||||
initMountPath: string
|
||||
|
||||
// +usage=Specify the extra volume mounts for the init container
|
||||
extraVolumeMounts: [...{
|
||||
// +usage=The name of the volume to be mounted
|
||||
name: string
|
||||
// +usage=The mountPath for mount in the init container
|
||||
mountPath: string
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,18 @@ spec:
|
||||
cue:
|
||||
template: |
|
||||
#K8sObject: {
|
||||
apiVersion: string
|
||||
kind: string
|
||||
metadata: {
|
||||
name: string
|
||||
...
|
||||
}
|
||||
// +usage=The resource type for the Kubernetes objects
|
||||
resource?: string
|
||||
// +usage=The group name for the Kubernetes objects
|
||||
group?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects with the name, exclusive to labelSelector
|
||||
name?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects from the namespace. Otherwise, fetch from the application's namespace.
|
||||
namespace?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects from the cluster. Otherwise, fetch from the local cluster.
|
||||
cluster?: string
|
||||
// +usage=If specified, fetch the Kubernetes objects according to the label selector, exclusive to name
|
||||
labelSelector?: [string]: string
|
||||
...
|
||||
}
|
||||
output: parameter.objects[0]
|
||||
@@ -30,7 +36,12 @@ spec:
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: objects: [...#K8sObject]
|
||||
parameter: {
|
||||
// +usage=If specified, application will fetch native Kubernetes objects according to the object description
|
||||
objects?: [...#K8sObject]
|
||||
// +usage=If specified, the objects in the urls will be loaded.
|
||||
urls?: [...string]
|
||||
}
|
||||
status:
|
||||
customStatus: |-
|
||||
if context.output.apiVersion == "apps/v1" && context.output.kind == "Deployment" {
|
||||
|
||||
@@ -14,10 +14,114 @@ spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
#Privileges: {
|
||||
// +usage=Specify the verbs to be allowed for the resource
|
||||
verbs: [...string]
|
||||
// +usage=Specify the apiGroups of the resource
|
||||
apiGroups?: [...string]
|
||||
// +usage=Specify the resources to be allowed
|
||||
resources?: [...string]
|
||||
// +usage=Specify the resourceNames to be allowed
|
||||
resourceNames?: [...string]
|
||||
// +usage=Specify the resource url to be allowed
|
||||
nonResourceURLs?: [...string]
|
||||
// +usage=Specify the scope of the privileges, default to be namespace scope
|
||||
scope: *"namespace" | "cluster"
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the name of ServiceAccount
|
||||
name: string
|
||||
// +usage=Specify whether to create new ServiceAccount or not
|
||||
create: *false | bool
|
||||
// +usage=Specify the privileges of the ServiceAccount, if not empty, RoleBindings(ClusterRoleBindings) will be created
|
||||
privileges?: [...#Privileges]
|
||||
}
|
||||
// +patchStrategy=retainKeys
|
||||
patch: spec: template: spec: serviceAccountName: parameter.name
|
||||
_clusterPrivileges: [ for p in parameter.privileges if p.scope == "cluster" {p}]
|
||||
_namespacePrivileges: [ for p in parameter.privileges if p.scope == "namespace" {p}]
|
||||
outputs: {
|
||||
if parameter.create {
|
||||
"service-account": {
|
||||
apiVersion: "v1"
|
||||
kind: "ServiceAccount"
|
||||
metadata: name: parameter.name
|
||||
}
|
||||
}
|
||||
if parameter.privileges != _|_ {
|
||||
if len(_clusterPrivileges) > 0 {
|
||||
"cluster-role": {
|
||||
apiVersion: "rbac.authorization.k8s.io/v1"
|
||||
kind: "ClusterRole"
|
||||
metadata: name: "\(context.namespace):\(parameter.name)"
|
||||
rules: [ for p in _clusterPrivileges {
|
||||
verbs: p.verbs
|
||||
if p.apiGroups != _|_ {
|
||||
apiGroups: p.apiGroups
|
||||
}
|
||||
if p.resources != _|_ {
|
||||
resources: p.resources
|
||||
}
|
||||
if p.resourceNames != _|_ {
|
||||
resourceNames: p.resourceNames
|
||||
}
|
||||
if p.nonResourceURLs != _|_ {
|
||||
nonResourceURLs: p.nonResourceURLs
|
||||
}
|
||||
}]
|
||||
}
|
||||
"cluster-role-binding": {
|
||||
apiVersion: "rbac.authorization.k8s.io/v1"
|
||||
kind: "ClusterRoleBinding"
|
||||
metadata: name: "\(context.namespace):\(parameter.name)"
|
||||
roleRef: {
|
||||
apiGroup: "rbac.authorization.k8s.io"
|
||||
kind: "ClusterRole"
|
||||
name: "\(context.namespace):\(parameter.name)"
|
||||
}
|
||||
subjects: [{
|
||||
kind: "ServiceAccount"
|
||||
name: parameter.name
|
||||
namespace: "\(context.namespace)"
|
||||
}]
|
||||
}
|
||||
}
|
||||
if len(_namespacePrivileges) > 0 {
|
||||
role: {
|
||||
apiVersion: "rbac.authorization.k8s.io/v1"
|
||||
kind: "Role"
|
||||
metadata: name: parameter.name
|
||||
rules: [ for p in _namespacePrivileges {
|
||||
verbs: p.verbs
|
||||
if p.apiGroups != _|_ {
|
||||
apiGroups: p.apiGroups
|
||||
}
|
||||
if p.resources != _|_ {
|
||||
resources: p.resources
|
||||
}
|
||||
if p.resourceNames != _|_ {
|
||||
resourceNames: p.resourceNames
|
||||
}
|
||||
if p.nonResourceURLs != _|_ {
|
||||
nonResourceURLs: p.nonResourceURLs
|
||||
}
|
||||
}]
|
||||
}
|
||||
"role-binding": {
|
||||
apiVersion: "rbac.authorization.k8s.io/v1"
|
||||
kind: "RoleBinding"
|
||||
metadata: name: parameter.name
|
||||
roleRef: {
|
||||
apiGroup: "rbac.authorization.k8s.io"
|
||||
kind: "Role"
|
||||
name: parameter.name
|
||||
}
|
||||
subjects: [{
|
||||
kind: "ServiceAccount"
|
||||
name: parameter.name
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +82,11 @@ spec:
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
// +usage=Specify the field reference for env
|
||||
fieldRef?: {
|
||||
// +usage=Specify the field path for env
|
||||
fieldPath: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ spec:
|
||||
{
|
||||
name: "pvc-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -73,6 +76,9 @@ spec:
|
||||
{
|
||||
name: "configmap-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -103,6 +109,9 @@ spec:
|
||||
{
|
||||
name: "secret-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -133,6 +142,9 @@ spec:
|
||||
{
|
||||
name: "emptydir-" + v.name
|
||||
mountPath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -141,12 +153,28 @@ spec:
|
||||
{
|
||||
name: "pvc-" + v.name
|
||||
devicePath: v.mountPath
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
}
|
||||
},
|
||||
] | []
|
||||
volumesList: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
|
||||
deDupVolumesArray: [
|
||||
for val in [
|
||||
for i, vi in volumesList {
|
||||
for j, vj in volumesList if j < i && vi.name == vj.name {
|
||||
_ignore: true
|
||||
}
|
||||
vi
|
||||
},
|
||||
] if val._ignore == _|_ {
|
||||
val
|
||||
},
|
||||
]
|
||||
patch: spec: template: spec: {
|
||||
// +patchKey=name
|
||||
volumes: pvcVolumesList + configMapVolumesList + secretVolumesList + emptyDirVolumesList
|
||||
volumes: deDupVolumesArray
|
||||
|
||||
containers: [{
|
||||
// +patchKey=name
|
||||
@@ -234,6 +262,7 @@ spec:
|
||||
name: string
|
||||
mountOnly: *false | bool
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
volumeMode: *"Filesystem" | string
|
||||
volumeName?: string
|
||||
accessModes: *["ReadWriteOnce"] | [...string]
|
||||
@@ -275,6 +304,7 @@ spec:
|
||||
configMapKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
data?: {...}
|
||||
@@ -298,6 +328,7 @@ spec:
|
||||
secretKey: string
|
||||
}]
|
||||
mountPath?: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
readOnly: *false | bool
|
||||
stringData?: {...}
|
||||
@@ -313,6 +344,7 @@ spec:
|
||||
emptyDir?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -149,14 +149,14 @@ spec:
|
||||
// +usage=Specifies a source the value of this var should come from
|
||||
valueFrom?: {
|
||||
// +usage=Selects a key of a secret in the pod's namespace
|
||||
secretKeyRef: {
|
||||
secretKeyRef?: {
|
||||
// +usage=The name of the secret in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the secret to select from. Must be a valid secret key
|
||||
key: string
|
||||
}
|
||||
// +usage=Selects a key of a config map in the pod's namespace
|
||||
configMapKeyRef: {
|
||||
configMapKeyRef?: {
|
||||
// +usage=The name of the config map in the pod's namespace to select from
|
||||
name: string
|
||||
// +usage=The key of the config map to select from. Must be a valid secret key
|
||||
|
||||
@@ -20,7 +20,10 @@ spec:
|
||||
for v in parameter.volumeMounts.pvc {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -29,7 +32,10 @@ spec:
|
||||
for v in parameter.volumeMounts.configMap {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -38,7 +44,10 @@ spec:
|
||||
for v in parameter.volumeMounts.secret {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -47,7 +56,10 @@ spec:
|
||||
for v in parameter.volumeMounts.emptyDir {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -56,7 +68,10 @@ spec:
|
||||
for v in parameter.volumeMounts.hostPath {
|
||||
{
|
||||
mountPath: v.mountPath
|
||||
name: v.name
|
||||
if v.subPath != _|_ {
|
||||
subPath: v.subPath
|
||||
}
|
||||
name: v.name
|
||||
}
|
||||
},
|
||||
] | []
|
||||
@@ -119,6 +134,19 @@ spec:
|
||||
},
|
||||
] | []
|
||||
}
|
||||
volumesList: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
|
||||
deDupVolumesArray: [
|
||||
for val in [
|
||||
for i, vi in volumesList {
|
||||
for j, vj in volumesList if j < i && vi.name == vj.name {
|
||||
_ignore: true
|
||||
}
|
||||
vi
|
||||
},
|
||||
] if val._ignore == _|_ {
|
||||
val
|
||||
},
|
||||
]
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
@@ -262,7 +290,7 @@ spec:
|
||||
}
|
||||
|
||||
if parameter["volumeMounts"] != _|_ {
|
||||
volumes: volumesArray.pvc + volumesArray.configMap + volumesArray.secret + volumesArray.emptyDir + volumesArray.hostPath
|
||||
volumes: deDupVolumesArray
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -375,6 +403,7 @@ spec:
|
||||
pvc?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
// +usage=The name of the PVC
|
||||
claimName: string
|
||||
}]
|
||||
@@ -382,6 +411,7 @@ spec:
|
||||
configMap?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
cmName: string
|
||||
items?: [...{
|
||||
@@ -394,6 +424,7 @@ spec:
|
||||
secret?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
defaultMode: *420 | int
|
||||
secretName: string
|
||||
items?: [...{
|
||||
@@ -406,12 +437,14 @@ spec:
|
||||
emptyDir?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
medium: *"" | "Memory"
|
||||
}]
|
||||
// +usage=Mount HostPath type volume
|
||||
hostPath?: [...{
|
||||
name: string
|
||||
mountPath: string
|
||||
subPath?: string
|
||||
path: string
|
||||
}]
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ metadata:
|
||||
name: {{ include "kubevela.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
controller.oam.dev/name: vela-core
|
||||
{{- include "kubevela.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
|
||||
@@ -203,7 +203,7 @@ admissionWebhooks:
|
||||
enabled: true
|
||||
image:
|
||||
repository: oamdev/kube-webhook-certgen
|
||||
tag: v2.4.0
|
||||
tag: v2.4.1
|
||||
pullPolicy: IfNotPresent
|
||||
nodeSelector: {}
|
||||
affinity: {}
|
||||
|
||||
@@ -25,3 +25,7 @@ spec:
|
||||
- name: my-mount
|
||||
mountPath: /test
|
||||
claimName: myclaim
|
||||
- name: my-mount
|
||||
mountPath: /test2
|
||||
subPath: /sub
|
||||
claimName: myclaim
|
||||
|
||||
@@ -16,8 +16,9 @@ spec:
|
||||
pvc:
|
||||
- name: test1
|
||||
mountPath: /test/mount/pvc
|
||||
- name: test2
|
||||
- name: test1
|
||||
mountPath: /test/mount2/pvc
|
||||
subPath: /sub
|
||||
configMap:
|
||||
- name: test1
|
||||
mountPath: /test/mount/cm
|
||||
|
||||
6
go.mod
6
go.mod
@@ -16,7 +16,7 @@ require (
|
||||
github.com/barnettZQG/inject v0.0.1
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869
|
||||
github.com/briandowns/spinner v1.11.1
|
||||
github.com/containerd/containerd v1.5.10
|
||||
github.com/containerd/containerd v1.5.13
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible
|
||||
github.com/coreos/prometheus-operator v0.41.1
|
||||
github.com/crossplane/crossplane-runtime v0.14.1-0.20210722005935-0b469fcc77cd
|
||||
@@ -46,7 +46,7 @@ require (
|
||||
github.com/hashicorp/hcl/v2 v2.9.1
|
||||
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/kubevela/prism v1.4.0
|
||||
github.com/kubevela/prism v1.4.1-0.20220613123457-94f1190f87c2
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.1
|
||||
github.com/oam-dev/cluster-gateway v1.4.0
|
||||
@@ -122,7 +122,7 @@ require (
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.23 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.24 // indirect
|
||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
|
||||
15
go.sum
15
go.sum
@@ -178,8 +178,8 @@ github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2
|
||||
github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
|
||||
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
|
||||
github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
|
||||
github.com/Microsoft/hcsshim v0.8.23 h1:47MSwtKGXet80aIn+7h4YI6fwPmwIghAnsx2aOUrG2M=
|
||||
github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=
|
||||
github.com/Microsoft/hcsshim v0.8.24 h1:jP+GMeRXIR1sH1kG4lJr9ShmSjVrua5jmFZDtfYGkn4=
|
||||
github.com/Microsoft/hcsshim v0.8.24/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
@@ -412,8 +412,9 @@ github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1
|
||||
github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
|
||||
github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
|
||||
github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
|
||||
github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ=
|
||||
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
|
||||
github.com/containerd/cgroups v1.0.3 h1:ADZftAkglvCiD44c77s5YmMqaP2pzVCFZvBmAlBdAP4=
|
||||
github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
|
||||
@@ -435,8 +436,8 @@ github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoT
|
||||
github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
|
||||
github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
|
||||
github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
|
||||
github.com/containerd/containerd v1.5.10 h1:3cQ2uRVCkJVcx5VombsE7105Gl9Wrl7ORAO3+4+ogf4=
|
||||
github.com/containerd/containerd v1.5.10/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ=
|
||||
github.com/containerd/containerd v1.5.13 h1:XqvKw9i4P7/mFrC3TSM7yV5cwFZ9avXe6M3YANKnzEE=
|
||||
github.com/containerd/containerd v1.5.13/go.mod h1:3AlCrzKROjIuP3JALsY14n8YtntaUDBu7vek+rPN5Vc=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
@@ -1343,8 +1344,8 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kubevela/prism v1.4.0 h1:wYCKXA3p9YpkcSsZjGnSEGBVL+3bPoZNEt4DYs3IxW4=
|
||||
github.com/kubevela/prism v1.4.0/go.mod h1:RP69+bRb57Occer6BeeF5zK3hrD1IhnYf2RNRsIdh9E=
|
||||
github.com/kubevela/prism v1.4.1-0.20220613123457-94f1190f87c2 h1:TaHlO4raKI3ehVSYY8QixYMHdI0VwKHY1KPNWcUre3I=
|
||||
github.com/kubevela/prism v1.4.1-0.20220613123457-94f1190f87c2/go.mod h1:RP69+bRb57Occer6BeeF5zK3hrD1IhnYf2RNRsIdh9E=
|
||||
github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U=
|
||||
github.com/kunwardeep/paralleltest v1.0.2/go.mod h1:ZPqNm1fVHPllh5LPVujzbVz1JN2GhLxSfY+oqUsvG30=
|
||||
github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4=
|
||||
|
||||
@@ -1058,21 +1058,22 @@ func Convert2SecName(name string) string {
|
||||
|
||||
// Installer helps addon enable, dependency-check, dispatch resources
|
||||
type Installer struct {
|
||||
ctx context.Context
|
||||
config *rest.Config
|
||||
addon *InstallPackage
|
||||
cli client.Client
|
||||
apply apply.Applicator
|
||||
r *Registry
|
||||
registryMeta map[string]SourceMeta
|
||||
args map[string]interface{}
|
||||
cache *Cache
|
||||
dc *discovery.DiscoveryClient
|
||||
ctx context.Context
|
||||
config *rest.Config
|
||||
addon *InstallPackage
|
||||
cli client.Client
|
||||
apply apply.Applicator
|
||||
r *Registry
|
||||
registryMeta map[string]SourceMeta
|
||||
args map[string]interface{}
|
||||
cache *Cache
|
||||
dc *discovery.DiscoveryClient
|
||||
skipVersionValidate bool
|
||||
}
|
||||
|
||||
// NewAddonInstaller will create an installer for addon
|
||||
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache) Installer {
|
||||
return Installer{
|
||||
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache, opts ...InstallOption) Installer {
|
||||
i := Installer{
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
cli: cli,
|
||||
@@ -1082,14 +1083,21 @@ func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *
|
||||
cache: cache,
|
||||
dc: discoveryClient,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&i)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (h *Installer) enableAddon(addon *InstallPackage) error {
|
||||
var err error
|
||||
h.addon = addon
|
||||
err = checkAddonVersionMeetRequired(h.ctx, addon.SystemRequirements, h.cli, h.dc)
|
||||
if err != nil {
|
||||
return VersionUnMatchError{addonName: addon.Name, err: err}
|
||||
|
||||
if !h.skipVersionValidate {
|
||||
err = checkAddonVersionMeetRequired(h.ctx, addon.SystemRequirements, h.cli, h.dc)
|
||||
if err != nil {
|
||||
return VersionUnMatchError{addonName: addon.Name, err: err}
|
||||
}
|
||||
}
|
||||
|
||||
if err = h.installDependency(addon); err != nil {
|
||||
@@ -1445,10 +1453,23 @@ func checkSemVer(actual string, require string) (bool, error) {
|
||||
}
|
||||
|
||||
func fetchVelaCoreImageTag(ctx context.Context, k8sClient client.Client) (string, error) {
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: types.KubeVelaControllerDeployment}, deploy); err != nil {
|
||||
deployList := &appsv1.DeploymentList{}
|
||||
if err := k8sClient.List(ctx, deployList, client.MatchingLabels{oam.LabelControllerName: oam.ApplicationControllerName}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
deploy := appsv1.Deployment{}
|
||||
if len(deployList.Items) == 0 {
|
||||
// backward compatible logic old version which vela-core controller has no this label
|
||||
if err := k8sClient.Get(ctx, types2.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: types.KubeVelaControllerDeployment}, &deploy); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return "", errors.New("can't find a running KubeVela instance, please install it first")
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
deploy = deployList.Items[0]
|
||||
}
|
||||
|
||||
var tag string
|
||||
for _, c := range deploy.Spec.Template.Spec.Containers {
|
||||
if c.Name == types.DefaultKubeVelaReleaseName {
|
||||
|
||||
@@ -196,7 +196,7 @@ var _ = Describe("Addon func test", func() {
|
||||
It("fetchVelaCoreImageTag func test", func() {
|
||||
deploy = appsv1.Deployment{}
|
||||
tag, err := fetchVelaCoreImageTag(ctx, k8sClient)
|
||||
Expect(err).Should(util.NotFoundMatcher{})
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
Expect(tag).Should(BeEquivalentTo(""))
|
||||
|
||||
Expect(yaml.Unmarshal([]byte(deployYaml), &deploy)).Should(BeNil())
|
||||
@@ -217,7 +217,7 @@ var _ = Describe("Addon func test", func() {
|
||||
|
||||
It("checkAddonVersionMeetRequired func test", func() {
|
||||
deploy = appsv1.Deployment{}
|
||||
Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.1"}, k8sClient, dc)).Should(util.NotFoundMatcher{})
|
||||
Expect(checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=v1.2.1"}, k8sClient, dc)).ShouldNot(BeNil())
|
||||
Expect(yaml.Unmarshal([]byte(deployYaml), &deploy)).Should(BeNil())
|
||||
deploy.SetNamespace(types.DefaultKubeVelaNS)
|
||||
Expect(k8sClient.Create(ctx, &deploy)).Should(BeNil())
|
||||
@@ -408,6 +408,8 @@ kind: Deployment
|
||||
metadata:
|
||||
name: kubevela-vela-core
|
||||
namespace: vela-system
|
||||
labels:
|
||||
controller.oam.dev/name: vela-core
|
||||
spec:
|
||||
progressDeadlineSeconds: 600
|
||||
replicas: 1
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/crossplane/crossplane-runtime/pkg/test"
|
||||
"github.com/google/go-github/v32/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -47,6 +48,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
version2 "github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
@@ -791,6 +793,33 @@ func TestCheckAddonVersionMeetRequired(t *testing.T) {
|
||||
MockGet: test.NewMockGetFn(nil, func(obj client.Object) error {
|
||||
return nil
|
||||
}),
|
||||
MockList: test.NewMockListFn(nil, func(obj client.ObjectList) error {
|
||||
robj := obj.(*appsv1.DeploymentList)
|
||||
list := &appsv1.DeploymentList{
|
||||
Items: []appsv1.Deployment{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
oam.LabelControllerName: oam.ApplicationControllerName,
|
||||
},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Image: "vela-core:v1.2.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
list.DeepCopyInto(robj)
|
||||
return nil
|
||||
}),
|
||||
}
|
||||
ctx := context.Background()
|
||||
assert.NoError(t, checkAddonVersionMeetRequired(ctx, &SystemRequirements{VelaVersion: ">=1.2.4"}, k8sClient, nil))
|
||||
|
||||
BIN
pkg/addon/example-1.0.1.tgz
Normal file
BIN
pkg/addon/example-1.0.1.tgz
Normal file
Binary file not shown.
@@ -54,8 +54,8 @@ const (
|
||||
)
|
||||
|
||||
// EnableAddon will enable addon with dependency check, source is where addon from.
|
||||
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache) error {
|
||||
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache)
|
||||
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache, opts ...InstallOption) error {
|
||||
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache, opts...)
|
||||
pkg, err := h.loadInstallPackage(name, version)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -93,7 +93,7 @@ func DisableAddon(ctx context.Context, cli client.Client, name string, config *r
|
||||
}
|
||||
|
||||
// EnableAddonByLocalDir enable an addon from local dir
|
||||
func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli client.Client, dc *discovery.DiscoveryClient, applicator apply.Applicator, config *rest.Config, args map[string]interface{}) error {
|
||||
func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli client.Client, dc *discovery.DiscoveryClient, applicator apply.Applicator, config *rest.Config, args map[string]interface{}, opts ...InstallOption) error {
|
||||
absDir, err := filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -112,7 +112,7 @@ func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli cli
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil)
|
||||
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil, opts...)
|
||||
needEnableAddonNames, err := h.checkDependency(pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -228,3 +228,11 @@ func usingAppsInfo(apps []v1beta1.Application) string {
|
||||
func IsVersionRegistry(r Registry) bool {
|
||||
return r.Helm != nil
|
||||
}
|
||||
|
||||
// InstallOption define additional option for installation
|
||||
type InstallOption func(installer *Installer)
|
||||
|
||||
// SkipValidateVersion means skip validating system version
|
||||
func SkipValidateVersion(installer *Installer) {
|
||||
installer.skipVersionValidate = true
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/form3tech-oss/jwt-go"
|
||||
@@ -63,17 +62,17 @@ func (u *User) ShortTableName() string {
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (u *User) PrimaryKey() string {
|
||||
return verifyUserValue(u.Name)
|
||||
return u.Name
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (u *User) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
if u.Name != "" {
|
||||
index["name"] = verifyUserValue(u.Name)
|
||||
index["name"] = u.Name
|
||||
}
|
||||
if u.Email != "" {
|
||||
index["email"] = verifyUserValue(u.Email)
|
||||
index["email"] = u.Email
|
||||
}
|
||||
return index
|
||||
}
|
||||
@@ -99,14 +98,14 @@ func (u *ProjectUser) ShortTableName() string {
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (u *ProjectUser) PrimaryKey() string {
|
||||
return fmt.Sprintf("%s-%s", u.ProjectName, verifyUserValue(u.Username))
|
||||
return fmt.Sprintf("%s-%s", u.ProjectName, u.Username)
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (u *ProjectUser) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
if u.Username != "" {
|
||||
index["username"] = verifyUserValue(u.Username)
|
||||
index["username"] = u.Username
|
||||
}
|
||||
if u.ProjectName != "" {
|
||||
index["projectName"] = u.ProjectName
|
||||
@@ -114,12 +113,6 @@ func (u *ProjectUser) Index() map[string]string {
|
||||
return index
|
||||
}
|
||||
|
||||
func verifyUserValue(v string) string {
|
||||
s := strings.ReplaceAll(v, "@", "-")
|
||||
s = strings.ReplaceAll(s, " ", "-")
|
||||
return strings.ToLower(s)
|
||||
}
|
||||
|
||||
// CustomClaims is the custom claims
|
||||
type CustomClaims struct {
|
||||
Username string `json:"username"`
|
||||
|
||||
@@ -29,6 +29,12 @@ func init() {
|
||||
RegisterModel(&WorkflowRecord{})
|
||||
}
|
||||
|
||||
// Finished means the workflow record is finished
|
||||
const Finished = "true"
|
||||
|
||||
// UnFinished means the workflow record is not finished
|
||||
const UnFinished = "false"
|
||||
|
||||
// Workflow application delivery database model
|
||||
type Workflow struct {
|
||||
BaseModel
|
||||
|
||||
@@ -507,14 +507,14 @@ func (c *applicationServiceImpl) UpdateApplication(ctx context.Context, app *mod
|
||||
func (c *applicationServiceImpl) ListRecords(ctx context.Context, appName string) (*apisv1.ListWorkflowRecordsResponse, error) {
|
||||
var record = model.WorkflowRecord{
|
||||
AppPrimaryKey: appName,
|
||||
Finished: "false",
|
||||
Finished: model.UnFinished,
|
||||
}
|
||||
records, err := c.Store.List(ctx, &record, &datastore.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(records) == 0 {
|
||||
record.Finished = "true"
|
||||
record.Finished = model.Finished
|
||||
records, err = c.Store.List(ctx, &record, &datastore.ListOptions{
|
||||
Page: 1,
|
||||
PageSize: 1,
|
||||
|
||||
@@ -40,7 +40,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/repository"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
@@ -78,7 +77,7 @@ var _ = Describe("Test application service function", func() {
|
||||
projectService = &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService}
|
||||
envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService}
|
||||
workflowService = &workflowServiceImpl{Store: ds, EnvService: envService}
|
||||
definitionService = &definitionServiceImpl{KubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.Background())}
|
||||
definitionService = &definitionServiceImpl{KubeClient: k8sClient}
|
||||
envBindingService = &envBindingServiceImpl{Store: ds, EnvService: envService, WorkflowService: workflowService, KubeClient: k8sClient, DefinitionService: definitionService}
|
||||
targetService = &targetServiceImpl{Store: ds, K8sClient: k8sClient}
|
||||
appService = &applicationServiceImpl{
|
||||
|
||||
@@ -60,7 +60,10 @@ var _ = Describe("Test cluster service function", func() {
|
||||
secret.Name = name
|
||||
secret.Namespace = prismclusterv1alpha1.StorageNamespace
|
||||
secret.SetAnnotations(map[string]string{prismclusterv1alpha1.AnnotationClusterAlias: alias})
|
||||
secret.SetLabels(map[string]string{clustergatewaycommon.LabelKeyClusterCredentialType: string(clustergatewayv1alpha1.CredentialTypeX509Certificate)})
|
||||
secret.SetLabels(map[string]string{
|
||||
clustergatewaycommon.LabelKeyClusterEndpointType: string(clustergatewayv1alpha1.ClusterEndpointTypeConst),
|
||||
clustergatewaycommon.LabelKeyClusterCredentialType: string(clustergatewayv1alpha1.CredentialTypeX509Certificate),
|
||||
})
|
||||
time.Sleep(time.Second)
|
||||
return k8sClient.Create(ctx, secret)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/pkg/errors"
|
||||
@@ -53,9 +52,11 @@ type DefinitionService interface {
|
||||
UpdateDefinitionStatus(ctx context.Context, name string, status apisv1.UpdateDefinitionStatusRequest) (*apisv1.DetailDefinitionResponse, error)
|
||||
}
|
||||
|
||||
// DefinitionHidden means the definition can not be used in VelaUX
|
||||
const DefinitionHidden = "true"
|
||||
|
||||
type definitionServiceImpl struct {
|
||||
KubeClient client.Client `inject:"kubeClient"`
|
||||
caches *utils.MemoryCacheStore
|
||||
}
|
||||
|
||||
// DefinitionQueryOption define a set of query options
|
||||
@@ -80,7 +81,7 @@ const (
|
||||
|
||||
// NewDefinitionService new definition service
|
||||
func NewDefinitionService() DefinitionService {
|
||||
return &definitionServiceImpl{caches: utils.NewMemoryCacheStore(context.Background())}
|
||||
return &definitionServiceImpl{}
|
||||
}
|
||||
|
||||
func (d *definitionServiceImpl) ListDefinitions(ctx context.Context, ops DefinitionQueryOption) ([]*apisv1.DefinitionBase, error) {
|
||||
@@ -95,9 +96,6 @@ func (d *definitionServiceImpl) ListDefinitions(ctx context.Context, ops Definit
|
||||
}
|
||||
|
||||
func (d *definitionServiceImpl) listDefinitions(ctx context.Context, list *unstructured.UnstructuredList, kind string, ops DefinitionQueryOption) ([]*apisv1.DefinitionBase, error) {
|
||||
if mc := d.caches.Get(ops.String()); mc != nil {
|
||||
return mc.([]*apisv1.DefinitionBase), nil
|
||||
}
|
||||
matchLabels := metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
@@ -146,9 +144,6 @@ func (d *definitionServiceImpl) listDefinitions(ctx context.Context, list *unstr
|
||||
}
|
||||
defs = append(defs, definition)
|
||||
}
|
||||
if ops.AppliedWorkloads == "" {
|
||||
d.caches.Put(ops.String(), defs, time.Minute*3)
|
||||
}
|
||||
return defs, nil
|
||||
}
|
||||
|
||||
@@ -240,30 +235,27 @@ func (d *definitionServiceImpl) DetailDefinition(ctx context.Context, name, defT
|
||||
if err := d.KubeClient.Get(ctx, k8stypes.NamespacedName{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: fmt.Sprintf("%s-schema-%s", defType, name),
|
||||
}, &cm); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil, bcode.ErrDefinitionNoSchema
|
||||
}
|
||||
}, &cm); err != nil && !apierrors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, ok := cm.Data[types.OpenapiV3JSONSchema]
|
||||
if !ok {
|
||||
return nil, bcode.ErrDefinitionNoSchema
|
||||
}
|
||||
schema := &openapi3.Schema{}
|
||||
if err := schema.UnmarshalJSON([]byte(data)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// render default ui schema
|
||||
defaultUISchema := renderDefaultUISchema(schema)
|
||||
// patch from custom ui schema
|
||||
customUISchema := d.renderCustomUISchema(ctx, name, defType, defaultUISchema)
|
||||
return &apisv1.DetailDefinitionResponse{
|
||||
definition := &apisv1.DetailDefinitionResponse{
|
||||
DefinitionBase: *base,
|
||||
APISchema: schema,
|
||||
UISchema: customUISchema,
|
||||
}, nil
|
||||
}
|
||||
data, ok := cm.Data[types.OpenapiV3JSONSchema]
|
||||
if ok {
|
||||
schema := &openapi3.Schema{}
|
||||
if err := schema.UnmarshalJSON([]byte(data)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
definition.APISchema = schema
|
||||
// render default ui schema
|
||||
defaultUISchema := renderDefaultUISchema(schema)
|
||||
// patch from custom ui schema
|
||||
definition.UISchema = d.renderCustomUISchema(ctx, name, defType, defaultUISchema)
|
||||
}
|
||||
|
||||
return definition, nil
|
||||
}
|
||||
|
||||
func (d *definitionServiceImpl) renderCustomUISchema(ctx context.Context, name, defType string, defaultSchema []*utils.UIParameter) []*utils.UIParameter {
|
||||
@@ -355,7 +347,7 @@ func (d *definitionServiceImpl) UpdateDefinitionStatus(ctx context.Context, name
|
||||
}
|
||||
if !exist && update.HiddenInUI {
|
||||
labels := def.GetLabels()
|
||||
labels[types.LabelDefinitionHidden] = "true"
|
||||
labels[types.LabelDefinitionHidden] = DefinitionHidden
|
||||
def.SetLabels(labels)
|
||||
if err := d.KubeClient.Update(ctx, def); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -44,7 +44,7 @@ var _ = Describe("Test namespace service functions", func() {
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
definitionService = &definitionServiceImpl{KubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())}
|
||||
definitionService = &definitionServiceImpl{KubeClient: k8sClient}
|
||||
err := k8sClient.Create(context.Background(), &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vela-system",
|
||||
@@ -215,7 +215,6 @@ var _ = Describe("Test namespace service functions", func() {
|
||||
It("Test update ui schema", func() {
|
||||
du := &definitionServiceImpl{
|
||||
KubeClient: k8sClient,
|
||||
caches: utils.NewMemoryCacheStore(context.Background()),
|
||||
}
|
||||
cdata, err := ioutil.ReadFile("./testdata/workflowstep-apply-object.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
@@ -235,7 +234,6 @@ var _ = Describe("Test namespace service functions", func() {
|
||||
It("Test update status of the definition", func() {
|
||||
du := &definitionServiceImpl{
|
||||
KubeClient: k8sClient,
|
||||
caches: utils.NewMemoryCacheStore(context.Background()),
|
||||
}
|
||||
detail, err := du.UpdateDefinitionStatus(context.TODO(), "apply-object", v1.UpdateDefinitionStatusRequest{
|
||||
DefinitionType: "workflowstep",
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/domain/repository"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/infrastructure/datastore"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils"
|
||||
)
|
||||
|
||||
var _ = Describe("Test envBindingService functions", func() {
|
||||
@@ -54,7 +53,7 @@ var _ = Describe("Test envBindingService functions", func() {
|
||||
projectService := &projectServiceImpl{Store: ds, K8sClient: k8sClient, RbacService: rbacService}
|
||||
envService = &envServiceImpl{Store: ds, KubeClient: k8sClient, ProjectService: projectService}
|
||||
workflowService = &workflowServiceImpl{Store: ds, KubeClient: k8sClient, EnvService: envService}
|
||||
definitionService = &definitionServiceImpl{KubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())}
|
||||
definitionService = &definitionServiceImpl{KubeClient: k8sClient}
|
||||
envBindingService = &envBindingServiceImpl{Store: ds, WorkflowService: workflowService, DefinitionService: definitionService, KubeClient: k8sClient, EnvService: envService}
|
||||
envBindingDemo1 = apisv1.EnvBinding{
|
||||
Name: "envbinding-dev",
|
||||
|
||||
228
pkg/apiserver/domain/service/image.go
Normal file
228
pkg/apiserver/domain/service/image.go
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
|
||||
)
|
||||
|
||||
// NewImageService create a image service instance
|
||||
func NewImageService() ImageService {
|
||||
return &imageImpl{}
|
||||
}
|
||||
|
||||
// ImageService the image service provide some handler functions about the docker image
|
||||
type ImageService interface {
|
||||
ListImageRepos(ctx context.Context, project string) ([]v1.ImageRegistry, error)
|
||||
GetImageInfo(ctx context.Context, project, secretName, imageName string) v1.ImageInfo
|
||||
}
|
||||
|
||||
type imageImpl struct {
|
||||
K8sClient client.Client `inject:"kubeClient"`
|
||||
}
|
||||
|
||||
// ListImageRepos list the image repositories via user configuration
|
||||
func (i *imageImpl) ListImageRepos(ctx context.Context, project string) ([]v1.ImageRegistry, error) {
|
||||
var secrets corev1.SecretList
|
||||
if err := i.K8sClient.List(ctx, &secrets, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{
|
||||
types.LabelConfigCatalog: types.VelaCoreConfig,
|
||||
types.LabelConfigType: types.ImageRegistry,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var repos []v1.ImageRegistry
|
||||
for _, secret := range secrets.Items {
|
||||
if secret.Labels[types.LabelConfigProject] == "" || secret.Labels[types.LabelConfigProject] == project {
|
||||
repos = append(repos, v1.ImageRegistry{
|
||||
Name: secret.Name,
|
||||
SecretName: secret.Name,
|
||||
Domain: secret.Labels[types.LabelConfigIdentifier],
|
||||
})
|
||||
}
|
||||
}
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
// GetImageInfo get the image info from image registry
|
||||
func (i *imageImpl) GetImageInfo(ctx context.Context, project, secretName, imageName string) v1.ImageInfo {
|
||||
var imageInfo = v1.ImageInfo{
|
||||
Name: imageName,
|
||||
}
|
||||
ref, err := name.ParseReference(imageName)
|
||||
if err != nil {
|
||||
imageInfo.Message = "The image name is invalid"
|
||||
return imageInfo
|
||||
}
|
||||
registryDomain := ref.Context().RegistryStr()
|
||||
imageInfo.Registry = registryDomain
|
||||
var secrets corev1.SecretList
|
||||
if err := i.K8sClient.List(ctx, &secrets, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{
|
||||
types.LabelConfigCatalog: types.VelaCoreConfig,
|
||||
types.LabelConfigType: types.ImageRegistry,
|
||||
types.LabelConfigIdentifier: registryDomain,
|
||||
}); err != nil {
|
||||
log.Logger.Warnf("fail to list the docker registries, %s", err.Error())
|
||||
}
|
||||
var selectSecret []*corev1.Secret
|
||||
var selectSecretNames []string
|
||||
// get info with specified secret
|
||||
if secretName != "" {
|
||||
for i, secret := range secrets.Items {
|
||||
if secret.Labels[types.LabelConfigProject] == "" || secret.Labels[types.LabelConfigProject] == project {
|
||||
if secretName == secret.Name {
|
||||
selectSecret = append(selectSecret, &secrets.Items[i])
|
||||
selectSecretNames = append(selectSecretNames, secret.Name)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get info with the secret which match the registry domain
|
||||
if selectSecret == nil {
|
||||
for i, secret := range secrets.Items {
|
||||
if secret.Labels[types.LabelConfigProject] == "" || secret.Labels[types.LabelConfigProject] == project {
|
||||
if secret.Labels[types.LabelConfigIdentifier] == registryDomain {
|
||||
selectSecret = append(selectSecret, &secrets.Items[i])
|
||||
selectSecretNames = append(selectSecretNames, secret.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var username, password string
|
||||
var insecure = false
|
||||
var useHTTP = false
|
||||
imageInfo.SecretNames = selectSecretNames
|
||||
if len(selectSecret) > 0 {
|
||||
insecure, useHTTP, username, password = getAccountFromSecret(*selectSecret[0], registryDomain)
|
||||
}
|
||||
err = getImageInfo(imageName, insecure, useHTTP, username, password, &imageInfo)
|
||||
if err != nil {
|
||||
imageInfo.Message = fmt.Sprintf("Fail to get the image info:%s", err.Error())
|
||||
}
|
||||
return imageInfo
|
||||
}
|
||||
|
||||
// getAccountFromSecret get the username and password from the secret of `kubernetes.io/dockerconfigjson` type
|
||||
// refer: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
|
||||
func getAccountFromSecret(secret corev1.Secret, registryDomain string) (insecure, useHTTP bool, username, password string) {
|
||||
if secret.Data != nil {
|
||||
// If users use the self-signed certificate, enable the insecure-skip-verify
|
||||
insecure = string(secret.Data["insecure-skip-verify"]) == "true"
|
||||
useHTTP = string(secret.Data["protocol-use-http"]) == "true"
|
||||
conf := secret.Data[".dockerconfigjson"]
|
||||
if len(conf) > 0 {
|
||||
var authConfig map[string]map[string]map[string]string
|
||||
if err := json.Unmarshal(conf, &authConfig); err != nil {
|
||||
log.Logger.Warnf("fail to unmarshal the secret %s , %s", secret.Name, err.Error())
|
||||
return
|
||||
}
|
||||
if authConfig != nil && authConfig["auths"] != nil && authConfig["auths"][registryDomain] != nil {
|
||||
data := authConfig["auths"][registryDomain]
|
||||
username = data["username"]
|
||||
password = data["password"]
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getImageInfo(imageName string, insecure, useHTTP bool, username, password string, info *v1.ImageInfo) error {
|
||||
var options []remote.Option
|
||||
if username != "" || password != "" {
|
||||
basic := &authn.Basic{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
options = append(options, remote.WithAuth(basic))
|
||||
}
|
||||
if insecure {
|
||||
options = append(options, remote.WithTransport(&http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
// By default we wrap the transport in retries, so reduce the
|
||||
// default dial timeout to 5s to avoid 5x 30s of connection
|
||||
// timeouts when doing the "ping" on certain http registries.
|
||||
Timeout: 5 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
// #nosec G402
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure},
|
||||
}))
|
||||
}
|
||||
|
||||
var parseOptions []name.Option
|
||||
if useHTTP {
|
||||
parseOptions = append(parseOptions, name.Insecure)
|
||||
}
|
||||
var err error
|
||||
ref, err := name.ParseReference(imageName, parseOptions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
image, err := remote.Image(ref, options...)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "incorrect username or password") {
|
||||
return fmt.Errorf("incorrect username or password")
|
||||
}
|
||||
var terr *transport.Error
|
||||
if errors.As(err, &terr) {
|
||||
fmt.Println(terr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
info.Manifest, err = image.Manifest()
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to get the manifest:%w", err)
|
||||
}
|
||||
info.Info, err = image.ConfigFile()
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to get the config:%w", err)
|
||||
}
|
||||
for _, l := range info.Manifest.Layers {
|
||||
info.Size += l.Size
|
||||
}
|
||||
info.Size += info.Manifest.Config.Size
|
||||
return nil
|
||||
}
|
||||
67
pkg/apiserver/domain/service/image_test.go
Normal file
67
pkg/apiserver/domain/service/image_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
velatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
v1 "github.com/oam-dev/kubevela/pkg/apiserver/interfaces/api/dto/v1"
|
||||
)
|
||||
|
||||
func TestGetImageInfo(t *testing.T) {
|
||||
|
||||
s2 := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "s2",
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
|
||||
velatypes.LabelConfigType: velatypes.ImageRegistry,
|
||||
velatypes.LabelConfigProject: "",
|
||||
velatypes.LabelConfigIdentifier: "index.docker.io",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"insecure-skip-verify": []byte("true"),
|
||||
".dockerconfigjson": []byte(`{"auths":{"index.docker.io":{"auth":"aHlicmlkY2xvdWRAcHJvZC5YTEyMw==","username":"xxx","password":"yyy"}}}`),
|
||||
},
|
||||
}
|
||||
|
||||
insecure, useHTTP, user, pass := getAccountFromSecret(*s2, "index.docker.io")
|
||||
assert.DeepEqual(t, user, "xxx")
|
||||
assert.DeepEqual(t, pass, "yyy")
|
||||
assert.DeepEqual(t, insecure, true)
|
||||
assert.DeepEqual(t, useHTTP, false)
|
||||
|
||||
var cf v1.ImageInfo
|
||||
// Test the public image
|
||||
err := getImageInfo("nginx", false, false, "", "", &cf)
|
||||
assert.DeepEqual(t, err, nil)
|
||||
assert.DeepEqual(t, cf.Info.Config.Entrypoint, []string{"/docker-entrypoint.sh"})
|
||||
|
||||
// Test the private image
|
||||
err = getImageInfo("nginx424ru823-should-not-existed", false, false, "abc", "efg", &cf)
|
||||
assert.DeepEqual(t, err.Error(), "incorrect username or password")
|
||||
|
||||
err = getImageInfo("text.registry/test-image", false, false, "", "", &cf)
|
||||
assert.DeepEqual(t, err != nil, true)
|
||||
}
|
||||
@@ -18,15 +18,12 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
|
||||
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -41,7 +38,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/utils/log"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
image "github.com/oam-dev/kubevela/pkg/utils/imageregistry"
|
||||
)
|
||||
|
||||
// ProjectService project manage service.
|
||||
@@ -59,7 +55,6 @@ type ProjectService interface {
|
||||
UpdateProjectUser(ctx context.Context, projectName string, userName string, req apisv1.UpdateProjectUserRequest) (*apisv1.ProjectUserBase, error)
|
||||
Init(ctx context.Context) error
|
||||
GetConfigs(ctx context.Context, projectName, configType string) ([]*apisv1.Config, error)
|
||||
ValidateImage(ctx context.Context, projectName, image string) (*apisv1.ImageResponse, error)
|
||||
}
|
||||
|
||||
type projectServiceImpl struct {
|
||||
@@ -623,57 +618,3 @@ func retrieveConfigFromApplication(a v1beta1.Application, project string) *apisv
|
||||
Description: a.Annotations[types.AnnotationConfigDescription],
|
||||
}
|
||||
}
|
||||
|
||||
func (p *projectServiceImpl) ValidateImage(ctx context.Context, projectName, image string) (*apisv1.ImageResponse, error) {
|
||||
return validateImage(ctx, p.K8sClient, projectName, image)
|
||||
}
|
||||
|
||||
func validateImage(ctx context.Context, k8sClient client.Client, project, imageName string) (*apisv1.ImageResponse, error) {
|
||||
var (
|
||||
secrets v1.SecretList
|
||||
username string
|
||||
password string
|
||||
imagePullSecret string
|
||||
)
|
||||
|
||||
ref, err := name.ParseReference(imageName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imageURL := ref.Context().RegistryStr()
|
||||
|
||||
if err := k8sClient.List(ctx, &secrets, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{
|
||||
types.LabelConfigCatalog: types.VelaCoreConfig,
|
||||
types.LabelConfigType: types.ImageRegistry,
|
||||
types.LabelConfigIdentifier: ref.Context().RegistryStr(),
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, s := range secrets.Items {
|
||||
if s.Labels[types.LabelConfigProject] == "" || s.Labels[types.LabelConfigProject] == project {
|
||||
conf := s.Data[".dockerconfigjson"]
|
||||
var auths map[string]map[string]map[string]string
|
||||
if err := json.Unmarshal(conf, &auths); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imagePullSecret = s.Name
|
||||
if auths["auths"] != nil && auths["auths"][imageURL] != nil {
|
||||
data := auths["auths"][imageURL]
|
||||
username = data["username"]
|
||||
password = data["password"]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
existed, err := image.IsExisted(username, password, imageName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apisv1.ImageResponse{
|
||||
Existed: existed,
|
||||
Secret: imagePullSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -18,20 +18,14 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
|
||||
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"gotest.tools/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
@@ -299,331 +293,3 @@ var _ = Describe("Test project service functions", func() {
|
||||
Expect(roles.Total).Should(BeEquivalentTo(0))
|
||||
})
|
||||
})
|
||||
|
||||
func TestProjectGetConfigs(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
corev1.AddToScheme(s)
|
||||
terraformapi.AddToScheme(s)
|
||||
|
||||
createdTime, _ := time.Parse(time.UnixDate, "Wed Apr 7 11:06:39 PST 2022")
|
||||
|
||||
app1 := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a1",
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
model.LabelSourceOfTruth: model.FromInner,
|
||||
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
|
||||
velatypes.LabelConfigType: "terraform-provider",
|
||||
"config.oam.dev/project": "p1",
|
||||
},
|
||||
CreationTimestamp: metav1.NewTime(createdTime),
|
||||
},
|
||||
Status: common.AppStatus{Phase: common.ApplicationRunning},
|
||||
}
|
||||
|
||||
app2 := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a2",
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
model.LabelSourceOfTruth: model.FromInner,
|
||||
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
|
||||
velatypes.LabelConfigType: "terraform-provider",
|
||||
},
|
||||
CreationTimestamp: metav1.NewTime(createdTime),
|
||||
},
|
||||
Status: common.AppStatus{Phase: common.ApplicationRunning},
|
||||
}
|
||||
|
||||
app3 := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a3",
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
model.LabelSourceOfTruth: model.FromInner,
|
||||
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
|
||||
velatypes.LabelConfigType: "dex-connector",
|
||||
"config.oam.dev/project": "p3",
|
||||
},
|
||||
CreationTimestamp: metav1.NewTime(createdTime),
|
||||
},
|
||||
Status: common.AppStatus{Phase: common.ApplicationRunning},
|
||||
}
|
||||
|
||||
provider1 := &terraformapi.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "provider1",
|
||||
Namespace: "default",
|
||||
CreationTimestamp: metav1.NewTime(createdTime),
|
||||
},
|
||||
Status: terraformapi.ProviderStatus{
|
||||
State: terraformtypes.ProviderIsReady,
|
||||
},
|
||||
}
|
||||
|
||||
provider2 := &terraformapi.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "provider2",
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{
|
||||
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
|
||||
},
|
||||
},
|
||||
Status: terraformapi.ProviderStatus{
|
||||
State: terraformtypes.ProviderIsNotReady,
|
||||
},
|
||||
}
|
||||
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(app1, app2, app3, provider1, provider2).Build()
|
||||
|
||||
h := &projectServiceImpl{K8sClient: k8sClient}
|
||||
|
||||
type args struct {
|
||||
projectName string
|
||||
configType string
|
||||
h ProjectService
|
||||
}
|
||||
|
||||
type want struct {
|
||||
configs []*apisv1.Config
|
||||
errMsg string
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{
|
||||
name: "project is matched",
|
||||
args: args{
|
||||
projectName: "p1",
|
||||
configType: "terraform-provider",
|
||||
h: h,
|
||||
},
|
||||
want: want{
|
||||
configs: []*apisv1.Config{{
|
||||
ConfigType: "terraform-provider",
|
||||
Name: "a1",
|
||||
Project: "p1",
|
||||
CreatedTime: &createdTime,
|
||||
ApplicationStatus: "running",
|
||||
Status: "Ready",
|
||||
}, {
|
||||
ConfigType: "terraform-provider",
|
||||
Name: "a2",
|
||||
Project: "",
|
||||
CreatedTime: &createdTime,
|
||||
ApplicationStatus: "running",
|
||||
Status: "Ready",
|
||||
}, {
|
||||
Name: "provider1",
|
||||
CreatedTime: &createdTime,
|
||||
Status: "Ready",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "project is not matched",
|
||||
args: args{
|
||||
projectName: "p999",
|
||||
configType: "terraform-provider",
|
||||
h: h,
|
||||
},
|
||||
want: want{
|
||||
configs: []*apisv1.Config{{
|
||||
ConfigType: "terraform-provider",
|
||||
Name: "a2",
|
||||
Project: "",
|
||||
CreatedTime: &createdTime,
|
||||
ApplicationStatus: "running",
|
||||
Status: "Ready",
|
||||
}, {
|
||||
Name: "provider1",
|
||||
CreatedTime: &createdTime,
|
||||
Status: "Ready",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "config type is empty",
|
||||
args: args{
|
||||
projectName: "p3",
|
||||
configType: "",
|
||||
h: h,
|
||||
},
|
||||
want: want{
|
||||
configs: []*apisv1.Config{{
|
||||
ConfigType: "terraform-provider",
|
||||
Name: "a2",
|
||||
Project: "",
|
||||
CreatedTime: &createdTime,
|
||||
ApplicationStatus: "running",
|
||||
Status: "Ready",
|
||||
}, {
|
||||
ConfigType: "dex-connector",
|
||||
Name: "a3",
|
||||
Project: "p3",
|
||||
CreatedTime: &createdTime,
|
||||
ApplicationStatus: "running",
|
||||
Status: "Ready",
|
||||
}, {
|
||||
Name: "provider1",
|
||||
CreatedTime: &createdTime,
|
||||
Status: "Ready",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "config type is dex",
|
||||
args: args{
|
||||
projectName: "p3",
|
||||
configType: "config-dex-connector",
|
||||
h: h,
|
||||
},
|
||||
want: want{
|
||||
configs: []*apisv1.Config{{
|
||||
ConfigType: "dex-connector",
|
||||
Name: "a3",
|
||||
Project: "p3",
|
||||
CreatedTime: &createdTime,
|
||||
ApplicationStatus: "running",
|
||||
Status: "Ready",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "config type is invalid",
|
||||
args: args{
|
||||
configType: "xxx",
|
||||
h: h,
|
||||
},
|
||||
want: want{
|
||||
errMsg: "unsupported config type",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := tc.args.h.GetConfigs(ctx, tc.args.projectName, tc.args.configType)
|
||||
if tc.want.errMsg != "" || err != nil {
|
||||
assert.ErrorContains(t, err, tc.want.errMsg)
|
||||
}
|
||||
assert.DeepEqual(t, got, tc.want.configs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateImage(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
corev1.AddToScheme(s)
|
||||
terraformapi.AddToScheme(s)
|
||||
|
||||
s1 := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "s1",
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
|
||||
velatypes.LabelConfigType: velatypes.ImageRegistry,
|
||||
velatypes.LabelConfigProject: "",
|
||||
velatypes.LabelConfigIdentifier: "abce34289jwerojwerofaf77.com789",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
".dockerconfigjson": []byte(`{"auths":{"abce34289jwerojwerofaf77.com789":{"auth":"aHlicmlkY2xvdWRAcHJvZC5YTEyMw==","username":"xxx","password":"yyy"}}}`),
|
||||
},
|
||||
}
|
||||
k8sClient1 := fake.NewClientBuilder().WithScheme(s).WithObjects(s1).Build()
|
||||
h1 := &projectServiceImpl{K8sClient: k8sClient1}
|
||||
|
||||
s2 := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "s2",
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
|
||||
velatypes.LabelConfigType: velatypes.ImageRegistry,
|
||||
velatypes.LabelConfigProject: "",
|
||||
velatypes.LabelConfigIdentifier: "index.docker.io",
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
".dockerconfigjson": []byte(`{"auths":{"index.docker.io":{"auth":"aHlicmlkY2xvdWRAcHJvZC5YTEyMw==","username":"xxx","password":"yyy"}}}`),
|
||||
},
|
||||
}
|
||||
|
||||
k8sClient2 := fake.NewClientBuilder().WithScheme(s).WithObjects(s2).Build()
|
||||
h2 := &projectServiceImpl{K8sClient: k8sClient2}
|
||||
|
||||
type args struct {
|
||||
project string
|
||||
imageName string
|
||||
h ProjectService
|
||||
}
|
||||
|
||||
type want struct {
|
||||
resp *apisv1.ImageResponse
|
||||
errMsg string
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
{
|
||||
name: "validate image",
|
||||
args: args{
|
||||
project: "p1",
|
||||
imageName: "nginx",
|
||||
h: h1,
|
||||
},
|
||||
want: want{
|
||||
resp: &apisv1.ImageResponse{
|
||||
Existed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid image",
|
||||
args: args{
|
||||
project: "p1",
|
||||
imageName: "abce34289jwerojwerofaf77.com789/d/e:v1",
|
||||
h: h1,
|
||||
},
|
||||
want: want{
|
||||
errMsg: "Get",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "private docker image",
|
||||
args: args{
|
||||
project: "p1",
|
||||
imageName: "nginx424ru823-should-not-existed",
|
||||
h: h2,
|
||||
},
|
||||
want: want{
|
||||
errMsg: "incorrect username or password",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := tc.args.h.ValidateImage(ctx, tc.args.project, tc.args.imageName)
|
||||
if tc.want.errMsg != "" || err != nil {
|
||||
assert.ErrorContains(t, err, tc.want.errMsg)
|
||||
}
|
||||
assert.DeepEqual(t, got, tc.want.resp)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,6 @@ var ResourceMaps = map[string]resourceMetadata{
|
||||
},
|
||||
"applicationTemplate": {},
|
||||
"config": {},
|
||||
"image": {},
|
||||
},
|
||||
pathName: "projectName",
|
||||
},
|
||||
|
||||
@@ -50,7 +50,7 @@ func InitServiceBean(c config.Config) []interface{} {
|
||||
return []interface{}{
|
||||
clusterService, rbacService, projectService, envService, targetService, workflowService, oamApplicationService,
|
||||
velaQLService, definitionService, addonService, envBindingService, systemInfoService, helmService, userService,
|
||||
authenticationService, configService, applicationService, webhookService,
|
||||
authenticationService, configService, applicationService, webhookService, NewImageService(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -79,5 +81,9 @@ func (v *velaQLServiceImpl) QueryView(ctx context.Context, velaQL string) (*apis
|
||||
log.Logger.Errorf("decode the velaQL response to json failure %s", err.Error())
|
||||
return nil, bcode.ErrParseQuery2Json
|
||||
}
|
||||
if strings.Contains(velaQL, "collect-logs") {
|
||||
enc, _ := base64.StdEncoding.DecodeString(resp["logs"].(string))
|
||||
resp["logs"] = string(enc)
|
||||
}
|
||||
return &resp, err
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
@@ -426,6 +427,11 @@ func (j *jfrogHandlerImpl) handle(ctx context.Context, webhookTrigger *model.App
|
||||
return nil, err
|
||||
}
|
||||
image := fmt.Sprintf("%s/%s:%s", jfrogReq.Data.RepoKey, jfrogReq.Data.ImageName, jfrogReq.Data.Tag)
|
||||
pathArray := strings.Split(jfrogReq.Data.Path, "/")
|
||||
if len(pathArray) > 2 {
|
||||
image = fmt.Sprintf("%s/%s:%s", jfrogReq.Data.RepoKey, strings.Join(pathArray[:len(pathArray)-2], "/"), jfrogReq.Data.Tag)
|
||||
}
|
||||
|
||||
if jfrogReq.Data.URL != "" {
|
||||
image = fmt.Sprintf("%s/%s", jfrogReq.Data.URL, image)
|
||||
}
|
||||
@@ -441,7 +447,7 @@ func (j *jfrogHandlerImpl) handle(ctx context.Context, webhookTrigger *model.App
|
||||
TriggerType: apisv1.TriggerTypeWebhook,
|
||||
Force: true,
|
||||
ImageInfo: &model.ImageInfo{
|
||||
Type: model.PayloadTypeHarbor,
|
||||
Type: model.PayloadTypeJFrog,
|
||||
Resource: &model.ImageResource{
|
||||
Digest: jfrogReq.Data.Digest,
|
||||
Tag: jfrogReq.Data.Tag,
|
||||
|
||||
@@ -322,7 +322,17 @@ func genClusterCountInfo(num int) string {
|
||||
return "<10"
|
||||
case num < 50:
|
||||
return "<50"
|
||||
case num < 100:
|
||||
return "<100"
|
||||
case num < 150:
|
||||
return "<150"
|
||||
case num < 200:
|
||||
return "<200"
|
||||
case num < 300:
|
||||
return "<300"
|
||||
case num < 500:
|
||||
return "<500"
|
||||
default:
|
||||
return ">=50"
|
||||
return ">=500"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,8 +211,28 @@ func TestGenClusterCountInfo(t *testing.T) {
|
||||
res: "<50",
|
||||
},
|
||||
{
|
||||
count: 100,
|
||||
res: ">=50",
|
||||
count: 90,
|
||||
res: "<100",
|
||||
},
|
||||
{
|
||||
count: 137,
|
||||
res: "<150",
|
||||
},
|
||||
{
|
||||
count: 170,
|
||||
res: "<200",
|
||||
},
|
||||
{
|
||||
count: 270,
|
||||
res: "<300",
|
||||
},
|
||||
{
|
||||
count: 400,
|
||||
res: "<500",
|
||||
},
|
||||
{
|
||||
count: 520,
|
||||
res: ">=500",
|
||||
},
|
||||
}
|
||||
for _, testcase := range testcases {
|
||||
|
||||
@@ -75,6 +75,7 @@ func generateName(entity datastore.Entity) string {
|
||||
// record the old ways here, it'll be migrated
|
||||
// name := fmt.Sprintf("veladatabase-%s-%s", entity.TableName(), entity.PrimaryKey())
|
||||
name := fmt.Sprintf("%s-%s", entity.ShortTableName(), entity.PrimaryKey())
|
||||
name = verifyValue(name)
|
||||
return strings.ReplaceAll(name, "_", "-")
|
||||
}
|
||||
|
||||
@@ -86,6 +87,9 @@ func (m *kubeapi) generateConfigMap(entity datastore.Entity) *corev1.ConfigMap {
|
||||
}
|
||||
labels["table"] = entity.TableName()
|
||||
labels["primaryKey"] = entity.PrimaryKey()
|
||||
for k, v := range labels {
|
||||
labels[k] = verifyValue(v)
|
||||
}
|
||||
var configMap = corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: generateName(entity),
|
||||
@@ -178,6 +182,9 @@ func (m *kubeapi) Put(ctx context.Context, entity datastore.Entity) error {
|
||||
}
|
||||
labels["table"] = entity.TableName()
|
||||
labels["primaryKey"] = entity.PrimaryKey()
|
||||
for k, v := range labels {
|
||||
labels[k] = verifyValue(v)
|
||||
}
|
||||
entity.SetUpdateTime(time.Now())
|
||||
var configMap corev1.ConfigMap
|
||||
if err := m.kubeClient.Get(ctx, types.NamespacedName{Namespace: m.namespace, Name: generateName(entity)}, &configMap); err != nil {
|
||||
@@ -345,7 +352,7 @@ func (m *kubeapi) List(ctx context.Context, entity datastore.Entity, op *datasto
|
||||
selector = selector.Add(*rq)
|
||||
|
||||
for k, v := range entity.Index() {
|
||||
rq, err := labels.NewRequirement(k, selection.Equals, []string{v})
|
||||
rq, err := labels.NewRequirement(k, selection.Equals, []string{verifyValue(v)})
|
||||
if err != nil {
|
||||
return nil, datastore.ErrIndexInvalid
|
||||
}
|
||||
@@ -353,7 +360,11 @@ func (m *kubeapi) List(ctx context.Context, entity datastore.Entity, op *datasto
|
||||
}
|
||||
if op != nil {
|
||||
for _, inFilter := range op.In {
|
||||
rq, err := labels.NewRequirement(inFilter.Key, selection.In, inFilter.Values)
|
||||
var values []string
|
||||
for _, value := range inFilter.Values {
|
||||
values = append(values, verifyValue(value))
|
||||
}
|
||||
rq, err := labels.NewRequirement(inFilter.Key, selection.In, values)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("new list requirement failure %s", err.Error())
|
||||
return nil, datastore.ErrIndexInvalid
|
||||
@@ -431,7 +442,7 @@ func (m *kubeapi) Count(ctx context.Context, entity datastore.Entity, filterOpti
|
||||
return 0, datastore.NewDBError(err)
|
||||
}
|
||||
for k, v := range entity.Index() {
|
||||
rq, err := labels.NewRequirement(k, selection.Equals, []string{v})
|
||||
rq, err := labels.NewRequirement(k, selection.Equals, []string{verifyValue(v)})
|
||||
if err != nil {
|
||||
return 0, datastore.ErrIndexInvalid
|
||||
}
|
||||
@@ -439,7 +450,11 @@ func (m *kubeapi) Count(ctx context.Context, entity datastore.Entity, filterOpti
|
||||
}
|
||||
if filterOptions != nil {
|
||||
for _, inFilter := range filterOptions.In {
|
||||
rq, err := labels.NewRequirement(inFilter.Key, selection.In, inFilter.Values)
|
||||
var values []string
|
||||
for _, value := range inFilter.Values {
|
||||
values = append(values, verifyValue(value))
|
||||
}
|
||||
rq, err := labels.NewRequirement(inFilter.Key, selection.In, values)
|
||||
if err != nil {
|
||||
return 0, datastore.ErrIndexInvalid
|
||||
}
|
||||
@@ -473,3 +488,9 @@ func (m *kubeapi) Count(ctx context.Context, entity datastore.Entity, filterOpti
|
||||
}
|
||||
return int64(len(items)), nil
|
||||
}
|
||||
|
||||
func verifyValue(v string) string {
|
||||
s := strings.ReplaceAll(v, "@", "-")
|
||||
s = strings.ReplaceAll(s, " ", "-")
|
||||
return strings.ToLower(s)
|
||||
}
|
||||
|
||||
@@ -246,4 +246,42 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
equal := cmp.Equal(err, datastore.ErrRecordNotExist, cmpopts.EquateErrors())
|
||||
Expect(equal).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Test verify index", func() {
|
||||
var usr = model.User{Name: "can@delete", Email: "xxx@xx.com"}
|
||||
err := kubeStore.Add(context.TODO(), &usr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
usr.Email = "change"
|
||||
err = kubeStore.Put(context.TODO(), &usr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = kubeStore.Get(context.TODO(), &usr)
|
||||
Expect(err).Should(BeNil())
|
||||
diff := cmp.Diff(usr.Email, "change")
|
||||
Expect(diff).Should(BeEmpty())
|
||||
|
||||
list, err := kubeStore.List(context.TODO(), &usr, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "name",
|
||||
Values: []string{"can@delete"},
|
||||
},
|
||||
}}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 1)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
|
||||
count, err := kubeStore.Count(context.TODO(), &usr, &datastore.FilterOptions{In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "name",
|
||||
Values: []string{"can@delete"},
|
||||
},
|
||||
}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(count).Should(Equal(int64(1)))
|
||||
|
||||
usr.Name = "can@delete"
|
||||
err = kubeStore.Delete(context.TODO(), &usr)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
registryv1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
@@ -1380,3 +1381,26 @@ type ChartRepoResponse struct {
|
||||
type ChartRepoResponseList struct {
|
||||
ChartRepoResponse []*ChartRepoResponse `json:"repos"`
|
||||
}
|
||||
|
||||
// ImageInfo the docker image info
|
||||
type ImageInfo struct {
|
||||
Name string `json:"name"`
|
||||
SecretNames []string `json:"secretNames"`
|
||||
Registry string `json:"registry"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Info *registryv1.ConfigFile `json:"info,omitempty"`
|
||||
Size int64 `json:"size"`
|
||||
Manifest *registryv1.Manifest `json:"manifest"`
|
||||
}
|
||||
|
||||
// ImageRegistry the image repository info
|
||||
type ImageRegistry struct {
|
||||
Name string `json:"name"`
|
||||
SecretName string `json:"secretName"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
// ListImageRegistryResponse the response struct of listing the image registries
|
||||
type ListImageRegistryResponse struct {
|
||||
Registries []ImageRegistry `json:"registries"`
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func InitAPIBean() []interface{} {
|
||||
RegisterAPIInterface(NewTargetAPIInterface())
|
||||
RegisterAPIInterface(NewVelaQLAPIInterface())
|
||||
RegisterAPIInterface(NewWebhookAPIInterface())
|
||||
RegisterAPIInterface(NewHelmAPIInterface())
|
||||
RegisterAPIInterface(NewRepositoryAPIInterface())
|
||||
|
||||
// Authentication
|
||||
RegisterAPIInterface(NewAuthenticationAPIInterface())
|
||||
|
||||
@@ -203,16 +203,6 @@ func (n *projectAPIInterface) GetWebServiceRoute() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]*apis.Config{}))
|
||||
|
||||
ws.Route(ws.GET("/{projectName}/validate_image").To(n.validateImage).
|
||||
Doc("validate an image in a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(n.RbacService.CheckPerm("project/image", "get")).
|
||||
Param(ws.QueryParameter("image", "image name").DataType("string")).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Returns(200, "OK", []*apis.ImageResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]*apis.ImageResponse{}))
|
||||
|
||||
ws.Filter(authCheckFilter)
|
||||
return ws
|
||||
}
|
||||
@@ -599,23 +589,3 @@ func (n *projectAPIInterface) getConfigs(req *restful.Request, res *restful.Resp
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectAPIInterface) validateImage(req *restful.Request, res *restful.Response) {
|
||||
resp, err := n.ProjectService.ValidateImage(req.Request.Context(), req.PathParameter("projectName"), req.QueryParameter("image"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if resp == nil {
|
||||
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(map[string]interface{}{"data": resp})
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
@@ -29,16 +28,18 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
type helmAPIInterface struct {
|
||||
HelmService service.HelmService `inject:""`
|
||||
type repositoryAPIInterface struct {
|
||||
HelmService service.HelmService `inject:""`
|
||||
ImageService service.ImageService `inject:""`
|
||||
RbacService service.RBACService `inject:""`
|
||||
}
|
||||
|
||||
// NewHelmAPIInterface will return helm APIInterface
|
||||
func NewHelmAPIInterface() Interface {
|
||||
return &helmAPIInterface{}
|
||||
// NewRepositoryAPIInterface will return the repository APIInterface
|
||||
func NewRepositoryAPIInterface() Interface {
|
||||
return &repositoryAPIInterface{}
|
||||
}
|
||||
|
||||
func (h helmAPIInterface) GetWebServiceRoute() *restful.WebService {
|
||||
func (h repositoryAPIInterface) GetWebServiceRoute() *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(versionPrefix+"/repository").
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
@@ -47,11 +48,12 @@ func (h helmAPIInterface) GetWebServiceRoute() *restful.WebService {
|
||||
|
||||
tags := []string{"repository", "helm"}
|
||||
|
||||
// List charts
|
||||
// List chart repos
|
||||
ws.Route(ws.GET("/chart_repos").To(h.listRepo).
|
||||
Doc("list chart repo").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("project", "the config project").DataType("string")).
|
||||
Param(ws.QueryParameter("project", "the config project").DataType("string").Required(true)).
|
||||
Filter(h.RbacService.CheckPerm("project/config", "list")).
|
||||
Returns(200, "OK", []string{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
@@ -86,11 +88,31 @@ func (h helmAPIInterface) GetWebServiceRoute() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
|
||||
ws.Route(ws.GET("/image/repos").To(h.getImageRepos).
|
||||
Doc("get the oci repos").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("project", "the config project").DataType("string").Required(true)).
|
||||
Filter(h.RbacService.CheckPerm("project/config", "list")).
|
||||
Returns(200, "OK", v1.ListImageRegistryResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
|
||||
ws.Route(ws.GET("/image/info").To(h.getImageInfo).
|
||||
Doc("get the oci repos").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("project", "the config project").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("name", "the image name").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("secretName", "the secret name of the image repository").DataType("string")).
|
||||
Filter(h.RbacService.CheckPerm("project/config", "list")).
|
||||
Returns(200, "OK", v1.ImageInfo{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]string{}))
|
||||
|
||||
ws.Filter(authCheckFilter)
|
||||
return ws
|
||||
}
|
||||
|
||||
func (h helmAPIInterface) listCharts(req *restful.Request, res *restful.Response) {
|
||||
func (h repositoryAPIInterface) listCharts(req *restful.Request, res *restful.Response) {
|
||||
url := utils.Sanitize(req.QueryParameter("repoUrl"))
|
||||
secName := utils.Sanitize(req.QueryParameter("secretName"))
|
||||
skipCache, err := isSkipCache(req)
|
||||
@@ -98,7 +120,7 @@ func (h helmAPIInterface) listCharts(req *restful.Request, res *restful.Response
|
||||
bcode.ReturnError(req, res, bcode.ErrSkipCacheParameter)
|
||||
return
|
||||
}
|
||||
charts, err := h.HelmService.ListChartNames(context.Background(), url, secName, skipCache)
|
||||
charts, err := h.HelmService.ListChartNames(req.Request.Context(), url, secName, skipCache)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -110,7 +132,7 @@ func (h helmAPIInterface) listCharts(req *restful.Request, res *restful.Response
|
||||
}
|
||||
}
|
||||
|
||||
func (h helmAPIInterface) listVersions(req *restful.Request, res *restful.Response) {
|
||||
func (h repositoryAPIInterface) listVersions(req *restful.Request, res *restful.Response) {
|
||||
url := req.QueryParameter("repoUrl")
|
||||
chartName := req.PathParameter("chart")
|
||||
secName := req.QueryParameter("secretName")
|
||||
@@ -120,7 +142,7 @@ func (h helmAPIInterface) listVersions(req *restful.Request, res *restful.Respon
|
||||
return
|
||||
}
|
||||
|
||||
versions, err := h.HelmService.ListChartVersions(context.Background(), url, chartName, secName, skipCache)
|
||||
versions, err := h.HelmService.ListChartVersions(req.Request.Context(), url, chartName, secName, skipCache)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -132,7 +154,7 @@ func (h helmAPIInterface) listVersions(req *restful.Request, res *restful.Respon
|
||||
}
|
||||
}
|
||||
|
||||
func (h helmAPIInterface) chartValues(req *restful.Request, res *restful.Response) {
|
||||
func (h repositoryAPIInterface) chartValues(req *restful.Request, res *restful.Response) {
|
||||
url := req.QueryParameter("repoUrl")
|
||||
secName := req.QueryParameter("secretName")
|
||||
chartName := req.PathParameter("chart")
|
||||
@@ -143,7 +165,7 @@ func (h helmAPIInterface) chartValues(req *restful.Request, res *restful.Respons
|
||||
return
|
||||
}
|
||||
|
||||
versions, err := h.HelmService.GetChartValues(context.Background(), url, chartName, version, secName, skipCache)
|
||||
versions, err := h.HelmService.GetChartValues(req.Request.Context(), url, chartName, version, secName, skipCache)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -155,9 +177,9 @@ func (h helmAPIInterface) chartValues(req *restful.Request, res *restful.Respons
|
||||
}
|
||||
}
|
||||
|
||||
func (h helmAPIInterface) listRepo(req *restful.Request, res *restful.Response) {
|
||||
func (h repositoryAPIInterface) listRepo(req *restful.Request, res *restful.Response) {
|
||||
project := req.QueryParameter("project")
|
||||
repos, err := h.HelmService.ListChartRepo(context.Background(), project)
|
||||
repos, err := h.HelmService.ListChartRepo(req.Request.Context(), project)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -169,6 +191,31 @@ func (h helmAPIInterface) listRepo(req *restful.Request, res *restful.Response)
|
||||
}
|
||||
}
|
||||
|
||||
func (h repositoryAPIInterface) getImageRepos(req *restful.Request, res *restful.Response) {
|
||||
project := req.QueryParameter("project")
|
||||
repos, err := h.ImageService.ListImageRepos(req.Request.Context(), project)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
err = res.WriteEntity(v1.ListImageRegistryResponse{Registries: repos})
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (h repositoryAPIInterface) getImageInfo(req *restful.Request, res *restful.Response) {
|
||||
project := req.QueryParameter("project")
|
||||
imageInfo := h.ImageService.GetImageInfo(req.Request.Context(), project, req.QueryParameter("secretName"), req.QueryParameter("name"))
|
||||
err := res.WriteEntity(imageInfo)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func isSkipCache(req *restful.Request) (bool, error) {
|
||||
skipStr := req.QueryParameter("skipCache")
|
||||
skipCache := false
|
||||
@@ -97,21 +97,21 @@ func (wl *Workload) EvalContext(ctx process.Context) error {
|
||||
}
|
||||
|
||||
// EvalStatus eval workload status
|
||||
func (wl *Workload) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) {
|
||||
func (wl *Workload) EvalStatus(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor) (string, error) {
|
||||
// if the standard workload is managed by trait always return empty message
|
||||
if wl.SkipApplyWorkload {
|
||||
return "", nil
|
||||
}
|
||||
return wl.engine.Status(ctx, cli, ns, wl.FullTemplate.CustomStatus, wl.Params)
|
||||
return wl.engine.Status(ctx, cli, accessor, wl.FullTemplate.CustomStatus, wl.Params)
|
||||
}
|
||||
|
||||
// EvalHealth eval workload health check
|
||||
func (wl *Workload) EvalHealth(ctx process.Context, client client.Client, namespace string) (bool, error) {
|
||||
func (wl *Workload) EvalHealth(ctx process.Context, client client.Client, accessor util.NamespaceAccessor) (bool, error) {
|
||||
// if health of template is not set or standard workload is managed by trait always return true
|
||||
if wl.FullTemplate.Health == "" || wl.SkipApplyWorkload {
|
||||
return true, nil
|
||||
}
|
||||
return wl.engine.HealthCheck(ctx, client, namespace, wl.FullTemplate.Health)
|
||||
return wl.engine.HealthCheck(ctx, client, accessor, wl.FullTemplate.Health)
|
||||
}
|
||||
|
||||
// Scope defines the scope of workload
|
||||
@@ -145,16 +145,16 @@ func (trait *Trait) EvalContext(ctx process.Context) error {
|
||||
}
|
||||
|
||||
// EvalStatus eval trait status
|
||||
func (trait *Trait) EvalStatus(ctx process.Context, cli client.Client, ns string) (string, error) {
|
||||
return trait.engine.Status(ctx, cli, ns, trait.CustomStatusFormat, trait.Params)
|
||||
func (trait *Trait) EvalStatus(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor) (string, error) {
|
||||
return trait.engine.Status(ctx, cli, accessor, trait.CustomStatusFormat, trait.Params)
|
||||
}
|
||||
|
||||
// EvalHealth eval trait health check
|
||||
func (trait *Trait) EvalHealth(ctx process.Context, client client.Client, namespace string) (bool, error) {
|
||||
func (trait *Trait) EvalHealth(ctx process.Context, client client.Client, accessor util.NamespaceAccessor) (bool, error) {
|
||||
if trait.FullTemplate.Health == "" {
|
||||
return true, nil
|
||||
}
|
||||
return trait.engine.HealthCheck(ctx, client, namespace, trait.HealthCheckPolicy)
|
||||
return trait.engine.HealthCheck(ctx, client, accessor, trait.HealthCheckPolicy)
|
||||
}
|
||||
|
||||
// Appfile describes application
|
||||
|
||||
@@ -47,6 +47,11 @@ func ContextWithUserInfo(ctx context.Context, app *v1beta1.Application) context.
|
||||
return request.WithUser(ctx, GetUserInfoInAnnotation(&app.ObjectMeta))
|
||||
}
|
||||
|
||||
// ContextClearUserInfo clear user info in context
|
||||
func ContextClearUserInfo(ctx context.Context) context.Context {
|
||||
return request.WithUser(ctx, nil)
|
||||
}
|
||||
|
||||
// SetUserInfoInAnnotation set username and group from userInfo into annotations
|
||||
// it will clear the existing service account annotation in avoid of permission leak
|
||||
func SetUserInfoInAnnotation(obj *metav1.ObjectMeta, userInfo authv1.UserInfo) {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/monitor/metrics"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcekeeper"
|
||||
)
|
||||
|
||||
@@ -215,17 +216,14 @@ func (h *AppHandler) ProduceArtifacts(ctx context.Context, comps []*types.Compon
|
||||
|
||||
// nolint
|
||||
func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Workload, appRev *v1beta1.ApplicationRevision, overrideNamespace string) (*common.ApplicationComponentStatus, bool, error) {
|
||||
namespace := h.app.Namespace
|
||||
if overrideNamespace != "" {
|
||||
namespace = overrideNamespace
|
||||
}
|
||||
accessor := util.NewApplicationResourceNamespaceAccessor(h.app.Namespace, overrideNamespace)
|
||||
|
||||
var (
|
||||
status = common.ApplicationComponentStatus{
|
||||
Name: wl.Name,
|
||||
WorkloadDefinition: wl.FullTemplate.Reference.Definition,
|
||||
Healthy: true,
|
||||
Namespace: namespace,
|
||||
Namespace: accessor.Namespace(),
|
||||
Cluster: multicluster.ClusterNameInContext(ctx),
|
||||
}
|
||||
appName = appRev.Spec.Application.Name
|
||||
@@ -235,10 +233,10 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
|
||||
|
||||
if wl.CapabilityCategory == types.TerraformCategory {
|
||||
var configuration terraforv1beta2.Configuration
|
||||
if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: namespace}, &configuration); err != nil {
|
||||
if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: accessor.Namespace()}, &configuration); err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
var legacyConfiguration terraforv1beta1.Configuration
|
||||
if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: namespace}, &legacyConfiguration); err != nil {
|
||||
if err := h.r.Client.Get(ctx, client.ObjectKey{Name: wl.Name, Namespace: accessor.Namespace()}, &legacyConfiguration); err != nil {
|
||||
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, check health error", appName, wl.Name)
|
||||
}
|
||||
isHealth = setStatus(&status, legacyConfiguration.Status.ObservedGeneration, legacyConfiguration.Generation,
|
||||
@@ -251,12 +249,12 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
|
||||
appRev.Name, configuration.Status.Apply.State, configuration.Status.Apply.Message)
|
||||
}
|
||||
} else {
|
||||
if ok, err := wl.EvalHealth(wl.Ctx, h.r.Client, namespace); !ok || err != nil {
|
||||
if ok, err := wl.EvalHealth(wl.Ctx, h.r.Client, accessor); !ok || err != nil {
|
||||
isHealth = false
|
||||
status.Healthy = false
|
||||
}
|
||||
|
||||
status.Message, err = wl.EvalStatus(wl.Ctx, h.r.Client, namespace)
|
||||
status.Message, err = wl.EvalStatus(wl.Ctx, h.r.Client, accessor)
|
||||
if err != nil {
|
||||
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, evaluate workload status message error", appName, wl.Name)
|
||||
}
|
||||
@@ -264,19 +262,26 @@ func (h *AppHandler) collectHealthStatus(ctx context.Context, wl *appfile.Worklo
|
||||
|
||||
var traitStatusList []common.ApplicationTraitStatus
|
||||
for _, tr := range wl.Traits {
|
||||
traitOverrideNamespace := overrideNamespace
|
||||
if tr.FullTemplate.TraitDefinition.Spec.ControlPlaneOnly {
|
||||
traitOverrideNamespace = appRev.GetNamespace()
|
||||
wl.Ctx.SetCtx(context.WithValue(wl.Ctx.GetCtx(), multicluster.ClusterContextKey, multicluster.ClusterLocalName))
|
||||
}
|
||||
_accessor := util.NewApplicationResourceNamespaceAccessor(h.app.Namespace, traitOverrideNamespace)
|
||||
var traitStatus = common.ApplicationTraitStatus{
|
||||
Type: tr.Name,
|
||||
Healthy: true,
|
||||
}
|
||||
if ok, err := tr.EvalHealth(wl.Ctx, h.r.Client, namespace); !ok || err != nil {
|
||||
if ok, err := tr.EvalHealth(wl.Ctx, h.r.Client, _accessor); !ok || err != nil {
|
||||
isHealth = false
|
||||
traitStatus.Healthy = false
|
||||
}
|
||||
traitStatus.Message, err = tr.EvalStatus(wl.Ctx, h.r.Client, namespace)
|
||||
traitStatus.Message, err = tr.EvalStatus(wl.Ctx, h.r.Client, _accessor)
|
||||
if err != nil {
|
||||
return nil, false, errors.WithMessagef(err, "app=%s, comp=%s, trait=%s, evaluate status message error", appName, wl.Name, tr.Name)
|
||||
}
|
||||
traitStatusList = append(traitStatusList, traitStatus)
|
||||
wl.Ctx.SetCtx(context.WithValue(wl.Ctx.GetCtx(), multicluster.ClusterContextKey, status.Cluster))
|
||||
}
|
||||
|
||||
status.Traits = traitStatusList
|
||||
@@ -299,12 +304,12 @@ func setStatus(status *common.ApplicationComponentStatus, observedGeneration, ge
|
||||
}
|
||||
return true
|
||||
}
|
||||
status.Message = message
|
||||
if !isLatest() || state != terraformtypes.Available {
|
||||
status.Healthy = false
|
||||
return false
|
||||
}
|
||||
status.Healthy = true
|
||||
status.Message = message
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/policy/envbinding"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/velaql/providers/query"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers/http"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers/kube"
|
||||
@@ -81,6 +82,7 @@ func (h *AppHandler) GenerateApplicationSteps(ctx monitorContext.Context,
|
||||
terraformProvider.Install(handlerProviders, app, func(comp common.ApplicationComponent) (*appfile.Workload, error) {
|
||||
return appParser.ParseWorkloadFromRevision(comp, appRev)
|
||||
})
|
||||
query.Install(handlerProviders, h.r.Client, nil)
|
||||
|
||||
var tasks []wfTypes.TaskRunner
|
||||
for _, step := range af.WorkflowSteps {
|
||||
@@ -188,7 +190,7 @@ func convertStepProperties(step *v1beta1.WorkflowStep, app *v1beta1.Application)
|
||||
}
|
||||
|
||||
func checkDependsOnValidComponent(dependsOnComponentNames, allComponentNames []string) (string, bool) {
|
||||
// does not depends on other components
|
||||
// does not depend on other components
|
||||
if dependsOnComponentNames == nil {
|
||||
return "", true
|
||||
}
|
||||
|
||||
@@ -967,7 +967,7 @@ func (h historiesByComponentRevision) Less(i, j int) bool {
|
||||
|
||||
// UpdateApplicationRevisionStatus update application revision status
|
||||
func (h *AppHandler) UpdateApplicationRevisionStatus(ctx context.Context, appRev *v1beta1.ApplicationRevision, succeed bool, wfStatus *common.WorkflowStatus) {
|
||||
if appRev == nil {
|
||||
if appRev == nil || DisableAllApplicationRevision {
|
||||
return
|
||||
}
|
||||
appRev.Status.Succeeded = succeed
|
||||
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
af "github.com/oam-dev/kubevela/pkg/appfile"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/process"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -478,7 +479,8 @@ func CUEBasedHealthCheck(ctx context.Context, c client.Client, wlRef WorkloadRef
|
||||
okToCheckTrait = true
|
||||
return
|
||||
}
|
||||
isHealthy, err := wl.EvalHealth(pCtx, c, ns)
|
||||
accessor := util.NewApplicationResourceNamespaceAccessor(ns, "")
|
||||
isHealthy, err := wl.EvalHealth(pCtx, c, accessor)
|
||||
if err != nil {
|
||||
wlHealth.HealthStatus = StatusUnhealthy
|
||||
wlHealth.Diagnosis = errors.Wrap(err, errHealthCheck).Error()
|
||||
@@ -490,7 +492,7 @@ func CUEBasedHealthCheck(ctx context.Context, c client.Client, wlRef WorkloadRef
|
||||
// TODO(wonderflow): we should add a custom way to let the template say why it's unhealthy, only a bool flag is not enough
|
||||
wlHealth.HealthStatus = StatusUnhealthy
|
||||
}
|
||||
wlHealth.CustomStatusMsg, err = wl.EvalStatus(pCtx, c, ns)
|
||||
wlHealth.CustomStatusMsg, err = wl.EvalStatus(pCtx, c, accessor)
|
||||
if err != nil {
|
||||
wlHealth.Diagnosis = errors.Wrap(err, errHealthCheck).Error()
|
||||
}
|
||||
@@ -522,7 +524,8 @@ func CUEBasedHealthCheck(ctx context.Context, c client.Client, wlRef WorkloadRef
|
||||
traits[i] = tHealth
|
||||
continue
|
||||
}
|
||||
isHealthy, err := tr.EvalHealth(pCtx, c, ns)
|
||||
accessor := util.NewApplicationResourceNamespaceAccessor("", ns)
|
||||
isHealthy, err := tr.EvalHealth(pCtx, c, accessor)
|
||||
if err != nil {
|
||||
tHealth.HealthStatus = StatusUnhealthy
|
||||
tHealth.Diagnosis = errors.Wrap(err, errHealthCheck).Error()
|
||||
@@ -535,7 +538,7 @@ func CUEBasedHealthCheck(ctx context.Context, c client.Client, wlRef WorkloadRef
|
||||
// TODO(wonderflow): we should add a custom way to let the template say why it's unhealthy, only a bool flag is not enough
|
||||
tHealth.HealthStatus = StatusUnhealthy
|
||||
}
|
||||
tHealth.CustomStatusMsg, err = tr.EvalStatus(pCtx, c, ns)
|
||||
tHealth.CustomStatusMsg, err = tr.EvalStatus(pCtx, c, accessor)
|
||||
if err != nil {
|
||||
tHealth.Diagnosis = errors.Wrap(err, errHealthCheck).Error()
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
"cuelang.org/go/cue/build"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/pkg/errors"
|
||||
git "gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -215,25 +215,26 @@ func GetOpenAPISchemaFromTerraformComponentDefinition(configuration string) ([]b
|
||||
|
||||
// GetTerraformConfigurationFromRemote gets Terraform Configuration(HCL)
|
||||
func GetTerraformConfigurationFromRemote(name, remoteURL, remotePath string) (string, error) {
|
||||
tmpPath := filepath.Join("./tmp/terraform", name)
|
||||
// Check if the directory exists. If yes, remove it.
|
||||
if _, err := os.Stat(tmpPath); err == nil {
|
||||
err := os.RemoveAll(tmpPath)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to remove the directory")
|
||||
}
|
||||
}
|
||||
_, err := git.PlainClone(tmpPath, false, &git.CloneOptions{
|
||||
URL: remoteURL,
|
||||
Progress: nil,
|
||||
})
|
||||
userHome, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cachePath := filepath.Join(userHome, ".vela", "terraform", name)
|
||||
// Check if the directory exists. If yes, remove it.
|
||||
entities, err := os.ReadDir(cachePath)
|
||||
if err != nil || len(entities) == 0 {
|
||||
fmt.Printf("loading terraform module %s into %s from %s\n", name, cachePath, remoteURL)
|
||||
if _, err = git.PlainClone(cachePath, false, &git.CloneOptions{
|
||||
URL: remoteURL,
|
||||
Progress: os.Stdout,
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
tfPath := filepath.Join(tmpPath, remotePath, "variables.tf")
|
||||
tfPath := filepath.Join(cachePath, remotePath, "variables.tf")
|
||||
if _, err := os.Stat(tfPath); err != nil {
|
||||
tfPath = filepath.Join(tmpPath, remotePath, "main.tf")
|
||||
tfPath = filepath.Join(cachePath, remotePath, "main.tf")
|
||||
if _, err := os.Stat(tfPath); err != nil {
|
||||
return "", errors.Wrap(err, "failed to find main.tf or variables.tf in Terraform configurations of the remote repository")
|
||||
}
|
||||
@@ -242,10 +243,6 @@ func GetTerraformConfigurationFromRemote(name, remoteURL, remotePath string) (st
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to read Terraform configuration")
|
||||
}
|
||||
if err := os.RemoveAll(tmpPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(conf), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,24 +17,16 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "github.com/agiledragon/gomonkey/v2"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/src-d/go-git.v4"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestGetTerraformConfigurationFromRemote(t *testing.T) {
|
||||
// If you hit a panic on macOS as below, please fix it by referencing https://github.com/eisenxp/macos-golink-wrapper.
|
||||
// panic: permission denied [recovered]
|
||||
// panic: permission denied
|
||||
type want struct {
|
||||
config string
|
||||
errMsg string
|
||||
@@ -46,8 +38,6 @@ func TestGetTerraformConfigurationFromRemote(t *testing.T) {
|
||||
path string
|
||||
data []byte
|
||||
variableFile string
|
||||
// mockWorkingPath will create `/tmp/terraform`
|
||||
mockWorkingPath bool
|
||||
}
|
||||
cases := map[string]struct {
|
||||
args args
|
||||
@@ -57,7 +47,7 @@ func TestGetTerraformConfigurationFromRemote(t *testing.T) {
|
||||
args: args{
|
||||
name: "valid",
|
||||
url: "https://github.com/kubevela-contrib/terraform-modules.git",
|
||||
path: "",
|
||||
path: "unittest/",
|
||||
data: []byte(`
|
||||
variable "aaa" {
|
||||
type = list(object({
|
||||
@@ -85,7 +75,7 @@ variable "aaa" {
|
||||
args: args{
|
||||
name: "aws-subnet",
|
||||
url: "https://github.com/kubevela-contrib/terraform-modules.git",
|
||||
path: "aws/subnet",
|
||||
path: "unittest/aws/subnet",
|
||||
data: []byte(`
|
||||
variable "aaa" {
|
||||
type = list(object({
|
||||
@@ -109,47 +99,20 @@ variable "aaa" {
|
||||
}`,
|
||||
},
|
||||
},
|
||||
"working path exists": {
|
||||
args: args{
|
||||
variableFile: "main.tf",
|
||||
mockWorkingPath: true,
|
||||
},
|
||||
want: want{
|
||||
errMsg: "failed to remove the directory",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if tc.args.mockWorkingPath {
|
||||
err := os.MkdirAll("./tmp/terraform", 0755)
|
||||
assert.NilError(t, err)
|
||||
defer os.RemoveAll("./tmp/terraform")
|
||||
patch1 := ApplyFunc(os.Remove, func(_ string) error {
|
||||
return errors.New("failed")
|
||||
})
|
||||
defer patch1.Reset()
|
||||
patch2 := ApplyFunc(os.Open, func(_ string) (*os.File, error) {
|
||||
return nil, errors.New("failed")
|
||||
})
|
||||
defer patch2.Reset()
|
||||
}
|
||||
|
||||
patch := ApplyFunc(git.PlainCloneContext, func(ctx context.Context, path string, isBare bool, o *git.CloneOptions) (*git.Repository, error) {
|
||||
var tmpPath string
|
||||
if tc.args.path != "" {
|
||||
tmpPath = filepath.Join("./tmp/terraform", tc.args.name, tc.args.path)
|
||||
} else {
|
||||
tmpPath = filepath.Join("./tmp/terraform", tc.args.name)
|
||||
}
|
||||
home, _ := os.UserHomeDir()
|
||||
path := filepath.Join(home, ".vela", "terraform")
|
||||
tmpPath := filepath.Join(path, tc.args.name, tc.args.path)
|
||||
if len(tc.args.data) > 0 {
|
||||
err := os.MkdirAll(tmpPath, os.ModePerm)
|
||||
assert.NilError(t, err)
|
||||
err = ioutil.WriteFile(filepath.Clean(filepath.Join(tmpPath, tc.args.variableFile)), tc.args.data, 0644)
|
||||
assert.NilError(t, err)
|
||||
return nil, nil
|
||||
})
|
||||
defer patch.Reset()
|
||||
}
|
||||
defer os.RemoveAll(tmpPath)
|
||||
|
||||
conf, err := GetTerraformConfigurationFromRemote(tc.args.name, tc.args.url, tc.args.path)
|
||||
if tc.want.errMsg != "" {
|
||||
@@ -62,8 +62,8 @@ const (
|
||||
// AbstractEngine defines Definition's Render interface
|
||||
type AbstractEngine interface {
|
||||
Complete(ctx process.Context, abstractTemplate string, params interface{}) error
|
||||
HealthCheck(ctx process.Context, cli client.Client, ns string, healthPolicyTemplate string) (bool, error)
|
||||
Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string, parameter interface{}) (string, error)
|
||||
HealthCheck(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, healthPolicyTemplate string) (bool, error)
|
||||
Status(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, customStatusTemplate string, parameter interface{}) (string, error)
|
||||
}
|
||||
|
||||
type def struct {
|
||||
@@ -151,7 +151,7 @@ func (wd *workloadDef) Complete(ctx process.Context, abstractTemplate string, pa
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader, ns string) (map[string]interface{}, error) {
|
||||
func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader, accessor util.NamespaceAccessor) (map[string]interface{}, error) {
|
||||
|
||||
var root = initRoot(ctx.BaseContextLabels())
|
||||
var commonLabels = GetCommonLabels(ctx.BaseContextLabels())
|
||||
@@ -162,7 +162,7 @@ func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader
|
||||
return nil, err
|
||||
}
|
||||
// workload main resource will have a unique label("app.oam.dev/resourceType"="WORKLOAD") in per component/app level
|
||||
object, err := getResourceFromObj(ctx.GetCtx(), componentWorkload, cli, ns, util.MergeMapOverrideWithDst(map[string]string{
|
||||
object, err := getResourceFromObj(ctx.GetCtx(), componentWorkload, cli, accessor.For(componentWorkload), util.MergeMapOverrideWithDst(map[string]string{
|
||||
oam.LabelOAMResourceType: oam.ResourceTypeWorkload,
|
||||
}, commonLabels), "")
|
||||
if err != nil {
|
||||
@@ -182,7 +182,7 @@ func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader
|
||||
return nil, err
|
||||
}
|
||||
// AuxiliaryWorkload will have a unique label("trait.oam.dev/resource"="name of outputs") in per component/app level
|
||||
object, err := getResourceFromObj(ctx.GetCtx(), traitRef, cli, ns, util.MergeMapOverrideWithDst(map[string]string{
|
||||
object, err := getResourceFromObj(ctx.GetCtx(), traitRef, cli, accessor.For(componentWorkload), util.MergeMapOverrideWithDst(map[string]string{
|
||||
oam.TraitTypeLabel: AuxiliaryWorkload,
|
||||
}, commonLabels), assist.Name)
|
||||
if err != nil {
|
||||
@@ -197,11 +197,11 @@ func (wd *workloadDef) getTemplateContext(ctx process.Context, cli client.Reader
|
||||
}
|
||||
|
||||
// HealthCheck address health check for workload
|
||||
func (wd *workloadDef) HealthCheck(ctx process.Context, cli client.Client, ns string, healthPolicyTemplate string) (bool, error) {
|
||||
func (wd *workloadDef) HealthCheck(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, healthPolicyTemplate string) (bool, error) {
|
||||
if healthPolicyTemplate == "" {
|
||||
return true, nil
|
||||
}
|
||||
templateContext, err := wd.getTemplateContext(ctx, cli, ns)
|
||||
templateContext, err := wd.getTemplateContext(ctx, cli, accessor)
|
||||
if err != nil {
|
||||
return false, errors.WithMessage(err, "get template context")
|
||||
}
|
||||
@@ -228,11 +228,11 @@ func checkHealth(templateContext map[string]interface{}, healthPolicyTemplate st
|
||||
}
|
||||
|
||||
// Status get workload status by customStatusTemplate
|
||||
func (wd *workloadDef) Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string, parameter interface{}) (string, error) {
|
||||
func (wd *workloadDef) Status(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, customStatusTemplate string, parameter interface{}) (string, error) {
|
||||
if customStatusTemplate == "" {
|
||||
return "", nil
|
||||
}
|
||||
templateContext, err := wd.getTemplateContext(ctx, cli, ns)
|
||||
templateContext, err := wd.getTemplateContext(ctx, cli, accessor)
|
||||
if err != nil {
|
||||
return "", errors.WithMessage(err, "get template context")
|
||||
}
|
||||
@@ -417,7 +417,7 @@ func initRoot(contextLabels map[string]string) map[string]interface{} {
|
||||
return root
|
||||
}
|
||||
|
||||
func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, ns string) (map[string]interface{}, error) {
|
||||
func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, accessor util.NamespaceAccessor) (map[string]interface{}, error) {
|
||||
var root = initRoot(ctx.BaseContextLabels())
|
||||
var commonLabels = GetCommonLabels(ctx.BaseContextLabels())
|
||||
|
||||
@@ -431,7 +431,7 @@ func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, n
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
object, err := getResourceFromObj(ctx.GetCtx(), traitRef, cli, ns, util.MergeMapOverrideWithDst(map[string]string{
|
||||
object, err := getResourceFromObj(ctx.GetCtx(), traitRef, cli, accessor.For(traitRef), util.MergeMapOverrideWithDst(map[string]string{
|
||||
oam.TraitTypeLabel: assist.Type,
|
||||
}, commonLabels), assist.Name)
|
||||
if err != nil {
|
||||
@@ -446,11 +446,11 @@ func (td *traitDef) getTemplateContext(ctx process.Context, cli client.Reader, n
|
||||
}
|
||||
|
||||
// Status get trait status by customStatusTemplate
|
||||
func (td *traitDef) Status(ctx process.Context, cli client.Client, ns string, customStatusTemplate string, parameter interface{}) (string, error) {
|
||||
func (td *traitDef) Status(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, customStatusTemplate string, parameter interface{}) (string, error) {
|
||||
if customStatusTemplate == "" {
|
||||
return "", nil
|
||||
}
|
||||
templateContext, err := td.getTemplateContext(ctx, cli, ns)
|
||||
templateContext, err := td.getTemplateContext(ctx, cli, accessor)
|
||||
if err != nil {
|
||||
return "", errors.WithMessage(err, "get template context")
|
||||
}
|
||||
@@ -458,11 +458,11 @@ func (td *traitDef) Status(ctx process.Context, cli client.Client, ns string, cu
|
||||
}
|
||||
|
||||
// HealthCheck address health check for trait
|
||||
func (td *traitDef) HealthCheck(ctx process.Context, cli client.Client, ns string, healthPolicyTemplate string) (bool, error) {
|
||||
func (td *traitDef) HealthCheck(ctx process.Context, cli client.Client, accessor util.NamespaceAccessor, healthPolicyTemplate string) (bool, error) {
|
||||
if healthPolicyTemplate == "" {
|
||||
return true, nil
|
||||
}
|
||||
templateContext, err := td.getTemplateContext(ctx, cli, ns)
|
||||
templateContext, err := td.getTemplateContext(ctx, cli, accessor)
|
||||
if err != nil {
|
||||
return false, errors.WithMessage(err, "get template context")
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ const (
|
||||
ContextComponents = "components"
|
||||
// ContextComponentType is the component type of current trait binding with
|
||||
ContextComponentType = "componentType"
|
||||
// ComponentRevisionPlaceHolder is the component revision name placeHolder, this field will be replace with real value
|
||||
// ComponentRevisionPlaceHolder is the component revision name placeHolder, this field will be replaced with real value
|
||||
// after component be created
|
||||
ComponentRevisionPlaceHolder = "KUBEVELA_COMPONENT_REVISION_PLACEHOLDER"
|
||||
// ContextDataArtifacts is used to store unstructured resources of components
|
||||
|
||||
@@ -302,7 +302,7 @@ func strategyUnify(baseFile *ast.File, patchFile *ast.File, params *UnifyParams,
|
||||
|
||||
ret := baseInst.Value().Unify(patchInst.Value())
|
||||
|
||||
rv, err := toString(ret)
|
||||
rv, err := toString(ret, removeTmpVar)
|
||||
if err != nil {
|
||||
return rv, errors.WithMessage(err, " format result toString")
|
||||
}
|
||||
@@ -358,7 +358,11 @@ func jsonMergePatch(base cue.Value, patch cue.Value) (string, error) {
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to merge base value and patch value by JsonMergePatch")
|
||||
}
|
||||
return string(merged), nil
|
||||
output, err := OpenBaiscLit(string(merged))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to parse open basic lit for merged result")
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func jsonPatch(base cue.Value, patch cue.Value) (string, error) {
|
||||
@@ -379,5 +383,9 @@ func jsonPatch(base cue.Value, patch cue.Value) (string, error) {
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to apply json patch")
|
||||
}
|
||||
return string(merged), nil
|
||||
output, err := OpenBaiscLit(string(merged))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to parse open basic lit for merged result")
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
@@ -358,3 +358,38 @@ func listOpen(expr ast.Node) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeTmpVar(expr ast.Node) ast.Node {
|
||||
switch v := expr.(type) {
|
||||
case *ast.File:
|
||||
for _, decl := range v.Decls {
|
||||
removeTmpVar(decl)
|
||||
}
|
||||
case *ast.Field:
|
||||
removeTmpVar(v.Value)
|
||||
case *ast.StructLit:
|
||||
var elts []ast.Decl
|
||||
for _, elt := range v.Elts {
|
||||
if field, isField := elt.(*ast.Field); isField {
|
||||
if ident, isIdent := field.Label.(*ast.Ident); isIdent && strings.HasPrefix(ident.Name, "_") {
|
||||
continue
|
||||
}
|
||||
}
|
||||
removeTmpVar(elt)
|
||||
elts = append(elts, elt)
|
||||
}
|
||||
v.Elts = elts
|
||||
case *ast.BinaryExpr:
|
||||
removeTmpVar(v.X)
|
||||
removeTmpVar(v.Y)
|
||||
case *ast.EmbedDecl:
|
||||
removeTmpVar(v.Expr)
|
||||
case *ast.Comprehension:
|
||||
removeTmpVar(v.Value)
|
||||
case *ast.ListLit:
|
||||
for _, elt := range v.Elts {
|
||||
removeTmpVar(elt)
|
||||
}
|
||||
}
|
||||
return expr
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"cuelang.org/go/cue/ast"
|
||||
"cuelang.org/go/cue/parser"
|
||||
"github.com/bmizerany/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWalk(t *testing.T) {
|
||||
@@ -128,3 +129,33 @@ func TestWalk(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRemoveTmpVar(t *testing.T) {
|
||||
src := `spec: {
|
||||
_tmp: "x"
|
||||
list: [{
|
||||
_tmp: "x"
|
||||
retain: "y"
|
||||
}, {
|
||||
_tmp: "x"
|
||||
retain: "z"
|
||||
}]
|
||||
retain: "y"
|
||||
}
|
||||
`
|
||||
r := require.New(t)
|
||||
var runtime cue.Runtime
|
||||
inst, err := runtime.Compile("-", src)
|
||||
r.NoError(err)
|
||||
s, err := toString(inst.Value(), removeTmpVar)
|
||||
r.NoError(err)
|
||||
r.Equal(`spec: {
|
||||
list: [{
|
||||
retain: "y"
|
||||
}, {
|
||||
retain: "z"
|
||||
}]
|
||||
retain: "y"
|
||||
}
|
||||
`, s)
|
||||
}
|
||||
|
||||
@@ -718,10 +718,10 @@ func TestImports(t *testing.T) {
|
||||
context: stepSessionID: "3w9qkdgn5w"`
|
||||
v, err := NewValue(`
|
||||
import (
|
||||
"vela/op"
|
||||
"vela/custom"
|
||||
)
|
||||
|
||||
id: op.context.stepSessionID
|
||||
id: custom.context.stepSessionID
|
||||
|
||||
`+cont, nil, cont)
|
||||
assert.NilError(t, err)
|
||||
|
||||
@@ -302,9 +302,6 @@ func (ctx *templateContext) GetCtx() context.Context {
|
||||
}
|
||||
|
||||
func (ctx *templateContext) SetCtx(newContext context.Context) {
|
||||
if ctx.ctx != nil {
|
||||
return
|
||||
}
|
||||
ctx.ctx = newContext
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/briandowns/spinner"
|
||||
prismclusterv1alpha1 "github.com/kubevela/prism/pkg/apis/cluster/v1alpha1"
|
||||
clusterv1alpha1 "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
|
||||
clustercommon "github.com/oam-dev/cluster-gateway/pkg/common"
|
||||
"github.com/oam-dev/cluster-register/pkg/hub"
|
||||
"github.com/oam-dev/cluster-register/pkg/spoke"
|
||||
"github.com/pkg/errors"
|
||||
@@ -36,11 +39,7 @@ import (
|
||||
ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
clusterv1alpha1 "github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
|
||||
clustercommon "github.com/oam-dev/cluster-gateway/pkg/common"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/policy/envbinding"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
velaerrors "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
@@ -49,6 +48,7 @@ import (
|
||||
|
||||
// KubeClusterConfig info for cluster management
|
||||
type KubeClusterConfig struct {
|
||||
FilePath string
|
||||
ClusterName string
|
||||
CreateNamespace string
|
||||
*clientcmdapi.Config
|
||||
@@ -84,15 +84,26 @@ func (clusterConfig *KubeClusterConfig) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterByVelaSecret create cluster secrets for KubeVela to use
|
||||
func (clusterConfig *KubeClusterConfig) RegisterByVelaSecret(ctx context.Context, cli client.Client) error {
|
||||
if err := ensureClusterNotExists(ctx, cli, clusterConfig.ClusterName); err != nil {
|
||||
return errors.Wrapf(err, "cannot use cluster name %s", clusterConfig.ClusterName)
|
||||
// PostRegistration try to create namespace after cluster registered. If failed, cluster will be unregistered.
|
||||
func (clusterConfig *KubeClusterConfig) PostRegistration(ctx context.Context, cli client.Client) error {
|
||||
if clusterConfig.CreateNamespace == "" {
|
||||
return nil
|
||||
}
|
||||
if err := ensureNamespaceExists(ctx, cli, clusterConfig.ClusterName, clusterConfig.CreateNamespace); err != nil {
|
||||
_ = DetachCluster(ctx, cli, clusterConfig.ClusterName, DetachClusterManagedClusterKubeConfigPathOption(clusterConfig.FilePath))
|
||||
return fmt.Errorf("failed to ensure %s namespace installed in cluster %s: %w", clusterConfig.CreateNamespace, clusterConfig.ClusterName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (clusterConfig *KubeClusterConfig) createClusterSecret(ctx context.Context, cli client.Client, withEndpoint bool) error {
|
||||
var credentialType clusterv1alpha1.CredentialType
|
||||
data := map[string][]byte{
|
||||
"endpoint": []byte(clusterConfig.Cluster.Server),
|
||||
"ca.crt": clusterConfig.Cluster.CertificateAuthorityData,
|
||||
data := map[string][]byte{}
|
||||
if withEndpoint {
|
||||
data["endpoint"] = []byte(clusterConfig.Cluster.Server)
|
||||
if !clusterConfig.Cluster.InsecureSkipTLSVerify {
|
||||
data["ca.crt"] = clusterConfig.Cluster.CertificateAuthorityData
|
||||
}
|
||||
}
|
||||
if len(clusterConfig.AuthInfo.Token) > 0 {
|
||||
credentialType = clusterv1alpha1.CredentialTypeServiceAccountToken
|
||||
@@ -113,22 +124,50 @@ func (clusterConfig *KubeClusterConfig) RegisterByVelaSecret(ctx context.Context
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
Data: data,
|
||||
}
|
||||
if err := cli.Create(ctx, secret); err != nil {
|
||||
return cli.Create(ctx, secret)
|
||||
}
|
||||
|
||||
// RegisterByVelaSecret create cluster secrets for KubeVela to use
|
||||
func (clusterConfig *KubeClusterConfig) RegisterByVelaSecret(ctx context.Context, cli client.Client) error {
|
||||
if err := ensureClusterNotExists(ctx, cli, clusterConfig.ClusterName); err != nil {
|
||||
return errors.Wrapf(err, "cannot use cluster name %s", clusterConfig.ClusterName)
|
||||
}
|
||||
if err := clusterConfig.createClusterSecret(ctx, cli, true); err != nil {
|
||||
return errors.Wrapf(err, "failed to add cluster to kubernetes")
|
||||
}
|
||||
// TODO(somefive): create namespace now only work for cluster secret
|
||||
if clusterConfig.CreateNamespace != "" {
|
||||
if err := ensureNamespaceExists(ctx, cli, clusterConfig.ClusterName, clusterConfig.CreateNamespace); err != nil {
|
||||
_ = cli.Delete(ctx, secret)
|
||||
return errors.Wrapf(err, "failed to ensure %s namespace installed in cluster %s", clusterConfig.CreateNamespace, clusterConfig.ClusterName)
|
||||
return clusterConfig.PostRegistration(ctx, cli)
|
||||
}
|
||||
|
||||
// CreateBootstrapConfigMapIfNotExists alternative to
|
||||
// https://github.com/kubernetes/kubernetes/blob/v1.24.1/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo/clusterinfo.go#L43
|
||||
func CreateBootstrapConfigMapIfNotExists(ctx context.Context, cli client.Client) error {
|
||||
cm := &corev1.ConfigMap{}
|
||||
key := apitypes.NamespacedName{Namespace: metav1.NamespacePublic, Name: "cluster-info"}
|
||||
if err := cli.Get(ctx, key, cm); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
cm.ObjectMeta = metav1.ObjectMeta{Namespace: key.Namespace, Name: key.Name}
|
||||
adminConfig, err := clientcmd.NewDefaultPathOptions().GetStartingConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
adminCluster := adminConfig.Contexts[adminConfig.CurrentContext].Cluster
|
||||
bs, err := clientcmd.Write(clientcmdapi.Config{
|
||||
Clusters: map[string]*clientcmdapi.Cluster{"": adminConfig.Clusters[adminCluster]},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cm.Data = map[string]string{"kubeconfig": string(bs)}
|
||||
return cli.Create(ctx, cm)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterClusterManagedByOCM create ocm managed cluster for use
|
||||
// TODO(somefive): OCM ManagedCluster only support cli join now
|
||||
func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.Context, args *JoinClusterArgs) error {
|
||||
func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.Context, cli client.Client, args *JoinClusterArgs) error {
|
||||
newTrackingSpinner := args.trackingSpinnerFactory
|
||||
hubCluster, err := hub.NewHubCluster(args.hubConfig)
|
||||
if err != nil {
|
||||
@@ -162,6 +201,9 @@ func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.
|
||||
return errors.Wrap(err, "fail to convert spoke-cluster kubeconfig")
|
||||
}
|
||||
|
||||
if err = CreateBootstrapConfigMapIfNotExists(ctx, cli); err != nil {
|
||||
return fmt.Errorf("failed to ensure cluster-info ConfigMap in kube-public namespace exists: %w", err)
|
||||
}
|
||||
spokeTracker := newTrackingSpinner("Building registration config for the managed cluster")
|
||||
spokeTracker.FinalMSG = "Successfully prepared registration config.\n"
|
||||
spokeTracker.Start()
|
||||
@@ -170,6 +212,7 @@ func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.
|
||||
args.ioStreams.Infof("Using the api endpoint from hub kubeconfig %q as registration entry.\n", args.hubConfig.Host)
|
||||
overridingRegistrationEndpoint = args.hubConfig.Host
|
||||
}
|
||||
|
||||
hubKubeToken, err := hubCluster.GenerateHubClusterKubeConfig(ctx, overridingRegistrationEndpoint)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "fail to generate the token for spoke-cluster")
|
||||
@@ -228,7 +271,7 @@ func (clusterConfig *KubeClusterConfig) RegisterClusterManagedByOCM(ctx context.
|
||||
|
||||
// LoadKubeClusterConfigFromFile create KubeClusterConfig from kubeconfig file
|
||||
func LoadKubeClusterConfigFromFile(filepath string) (*KubeClusterConfig, error) {
|
||||
clusterConfig := &KubeClusterConfig{}
|
||||
clusterConfig := &KubeClusterConfig{FilePath: filepath}
|
||||
var err error
|
||||
clusterConfig.Config, err = clientcmd.LoadFromFile(filepath)
|
||||
if err != nil {
|
||||
@@ -343,7 +386,7 @@ func JoinClusterByKubeConfig(ctx context.Context, cli client.Client, kubeconfigP
|
||||
return nil, errors.Wrapf(err, "failed to determine the registration endpoint for the hub cluster "+
|
||||
"when parsing --in-cluster-bootstrap flag")
|
||||
}
|
||||
if err = clusterConfig.RegisterClusterManagedByOCM(ctx, args); err != nil {
|
||||
if err = clusterConfig.RegisterClusterManagedByOCM(ctx, cli, args); err != nil {
|
||||
return clusterConfig, err
|
||||
}
|
||||
}
|
||||
@@ -382,12 +425,12 @@ func DetachCluster(ctx context.Context, cli client.Client, clusterName string, o
|
||||
if clusterName == ClusterLocalName {
|
||||
return ErrReservedLocalClusterName
|
||||
}
|
||||
vc, err := GetVirtualCluster(ctx, cli, clusterName)
|
||||
vc, err := prismclusterv1alpha1.NewClusterClient(cli).Get(ctx, clusterName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch vc.Type {
|
||||
switch vc.Spec.CredentialType {
|
||||
case clusterv1alpha1.CredentialTypeX509Certificate, clusterv1alpha1.CredentialTypeServiceAccountToken:
|
||||
clusterSecret, err := getMutableClusterSecret(ctx, cli, clusterName)
|
||||
if err != nil {
|
||||
@@ -396,7 +439,7 @@ func DetachCluster(ctx context.Context, cli client.Client, clusterName string, o
|
||||
if err := cli.Delete(ctx, clusterSecret); err != nil {
|
||||
return errors.Wrapf(err, "failed to detach cluster %s", clusterName)
|
||||
}
|
||||
case types.CredentialTypeOCMManagedCluster:
|
||||
case prismclusterv1alpha1.CredentialTypeOCMManagedCluster:
|
||||
if args.managedClusterKubeConfigPath == "" {
|
||||
return errors.New("kubeconfig-path must be set to detach ocm managed cluster")
|
||||
}
|
||||
@@ -414,7 +457,7 @@ func DetachCluster(ctx context.Context, cli client.Client, clusterName string, o
|
||||
return err
|
||||
}
|
||||
managedCluster := ocmclusterv1.ManagedCluster{ObjectMeta: metav1.ObjectMeta{Name: clusterName}}
|
||||
if err = cli.Delete(context.Background(), &managedCluster); err != nil {
|
||||
if err = cli.Delete(ctx, &managedCluster); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ const (
|
||||
LabelProject = "core.oam.dev/project"
|
||||
|
||||
LabelResourceRules = "rules.oam.dev/resources"
|
||||
|
||||
// LabelControllerName indicates the controller name
|
||||
LabelControllerName = "controller.oam.dev/name"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -953,3 +953,38 @@ func AsController(r *corev1.ObjectReference) metav1.OwnerReference {
|
||||
ref.Controller = &c
|
||||
return ref
|
||||
}
|
||||
|
||||
// NamespaceAccessor namespace accessor for resource
|
||||
type NamespaceAccessor interface {
|
||||
For(obj client.Object) string
|
||||
Namespace() string
|
||||
}
|
||||
|
||||
type applicationResourceNamespaceAccessor struct {
|
||||
applicationNamespace string
|
||||
overrideNamespace string
|
||||
}
|
||||
|
||||
// For access namespace for resource
|
||||
func (accessor *applicationResourceNamespaceAccessor) For(obj client.Object) string {
|
||||
if accessor.overrideNamespace != "" {
|
||||
return accessor.overrideNamespace
|
||||
}
|
||||
if originalNamespace := obj.GetNamespace(); originalNamespace != "" {
|
||||
return originalNamespace
|
||||
}
|
||||
return accessor.applicationNamespace
|
||||
}
|
||||
|
||||
// Namespace the namespace by default
|
||||
func (accessor *applicationResourceNamespaceAccessor) Namespace() string {
|
||||
if accessor.overrideNamespace != "" {
|
||||
return accessor.overrideNamespace
|
||||
}
|
||||
return accessor.applicationNamespace
|
||||
}
|
||||
|
||||
// NewApplicationResourceNamespaceAccessor create namespace accessor for resource in application
|
||||
func NewApplicationResourceNamespaceAccessor(appNs, overrideNs string) NamespaceAccessor {
|
||||
return &applicationResourceNamespaceAccessor{applicationNamespace: appNs, overrideNamespace: overrideNs}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@ limitations under the License.
|
||||
|
||||
package oam
|
||||
|
||||
// SystemDefinitonNamespace golbal value for controller and webhook systemlevel namespace
|
||||
var (
|
||||
// SystemDefinitonNamespace golbal value for controller and webhook systemlevel namespace
|
||||
SystemDefinitonNamespace string = "vela-system"
|
||||
|
||||
// ApplicationControllerName means the controller is application
|
||||
ApplicationControllerName string = "vela-core"
|
||||
)
|
||||
|
||||
@@ -49,6 +49,7 @@ func newDeleteConfig(options ...DeleteOption) *deleteConfig {
|
||||
|
||||
// Delete delete resources
|
||||
func (h *resourceKeeper) Delete(ctx context.Context, manifests []*unstructured.Unstructured, options ...DeleteOption) (err error) {
|
||||
h.ClearNamespaceForClusterScopedResources(manifests)
|
||||
if err = h.AdmissionCheck(ctx, manifests); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ func (h *resourceKeeper) Dispatch(ctx context.Context, manifests []*unstructured
|
||||
if h.applyOncePolicy != nil && h.applyOncePolicy.Enable && h.applyOncePolicy.Rules == nil {
|
||||
options = append(options, MetaOnlyOption{})
|
||||
}
|
||||
h.ClearNamespaceForClusterScopedResources(manifests)
|
||||
// 0. check admission
|
||||
if err = h.AdmissionCheck(ctx, manifests); err != nil {
|
||||
return err
|
||||
@@ -104,6 +105,7 @@ func (h *resourceKeeper) record(ctx context.Context, manifests []*unstructured.U
|
||||
}
|
||||
|
||||
cfg := newDispatchConfig(options...)
|
||||
ctx = auth.ContextClearUserInfo(ctx)
|
||||
if len(rootManifests) != 0 {
|
||||
rt, err := h.getRootRT(ctx)
|
||||
if err != nil {
|
||||
|
||||
35
pkg/resourcekeeper/utils.go
Normal file
35
pkg/resourcekeeper/utils.go
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resourcekeeper
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// ClearNamespaceForClusterScopedResources clear namespace for cluster scoped resources
|
||||
func (h *resourceKeeper) ClearNamespaceForClusterScopedResources(manifests []*unstructured.Unstructured) {
|
||||
for _, manifest := range manifests {
|
||||
mappings, err := h.Client.RESTMapper().RESTMappings(manifest.GroupVersionKind().GroupKind(), manifest.GroupVersionKind().Version)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if len(mappings) > 0 && mappings[0].Scope.Name() == meta.RESTScopeNameRoot {
|
||||
manifest.SetNamespace("")
|
||||
}
|
||||
}
|
||||
}
|
||||
55
pkg/resourcekeeper/utils_test.go
Normal file
55
pkg/resourcekeeper/utils_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resourcekeeper
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
var _ = Describe("Test ResourceKeeper utilities", func() {
|
||||
|
||||
It("Test ClearNamespaceForClusterScopedResources", func() {
|
||||
cli := testClient
|
||||
h := &resourceKeeper{
|
||||
Client: cli,
|
||||
app: &v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Name: "app", Namespace: "default"}},
|
||||
applicator: apply.NewAPIApplicator(cli),
|
||||
cache: newResourceCache(cli),
|
||||
}
|
||||
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "example", Namespace: "vela"}}
|
||||
nsObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ns)
|
||||
Expect(err).Should(Succeed())
|
||||
cm := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "example", Namespace: "vela"}}
|
||||
cmObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(cm)
|
||||
Expect(err).Should(Succeed())
|
||||
uns := []*unstructured.Unstructured{{Object: nsObj}, {Object: cmObj}}
|
||||
uns[0].SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Namespace"))
|
||||
uns[1].SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("ConfigMap"))
|
||||
h.ClearNamespaceForClusterScopedResources(uns)
|
||||
Expect(uns[0].GetNamespace()).Should(Equal(""))
|
||||
Expect(uns[1].GetNamespace()).Should(Equal("vela"))
|
||||
})
|
||||
|
||||
})
|
||||
@@ -163,6 +163,10 @@ func listApplicationResourceTrackers(ctx context.Context, cli client.Client, app
|
||||
}
|
||||
|
||||
// ListApplicationResourceTrackers list resource trackers for application with all historyRTs sorted by version number
|
||||
// rootRT -> The ResourceTracker that records life-long resources. These resources will only be recycled when application is removed.
|
||||
// currentRT -> The ResourceTracker that tracks the resources used by the latest version of application.
|
||||
// historyRTs -> The ResourceTrackers that tracks the resources in outdated versions.
|
||||
// crRT -> The ResourceTracker that tracks the component revisions created by the application.
|
||||
func ListApplicationResourceTrackers(ctx context.Context, cli client.Client, app *v1beta1.Application) (rootRT *v1beta1.ResourceTracker, currentRT *v1beta1.ResourceTracker, historyRTs []*v1beta1.ResourceTracker, crRT *v1beta1.ResourceTracker, err error) {
|
||||
metrics.ListResourceTrackerCounter.WithLabelValues("application").Inc()
|
||||
rts, err := listApplicationResourceTrackers(ctx, cli, app)
|
||||
|
||||
@@ -85,10 +85,7 @@ func (options *ResourceTreePrintOptions) loadResourceRows(currentRT *v1beta1.Res
|
||||
if mr.Deleted {
|
||||
continue
|
||||
}
|
||||
rows = append(rows, &resourceRow{
|
||||
mr: mr.DeepCopy(),
|
||||
status: resourceRowStatusUpdated,
|
||||
})
|
||||
rows = append(rows, buildResourceRow(mr, resourceRowStatusUpdated))
|
||||
}
|
||||
}
|
||||
for _, rt := range historyRT {
|
||||
@@ -100,10 +97,7 @@ func (options *ResourceTreePrintOptions) loadResourceRows(currentRT *v1beta1.Res
|
||||
}
|
||||
}
|
||||
if matchedRow == nil {
|
||||
rows = append(rows, &resourceRow{
|
||||
mr: mr.DeepCopy(),
|
||||
status: resourceRowStatusOutdated,
|
||||
})
|
||||
rows = append(rows, buildResourceRow(mr, resourceRowStatusOutdated))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -410,3 +404,14 @@ func RetrieveKubeCtlGetMessageGenerator(cfg *rest.Config) (ResourceDetailRetriev
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildResourceRow(mr v1beta1.ManagedResource, resourceStatus string) *resourceRow {
|
||||
rr := &resourceRow{
|
||||
mr: mr.DeepCopy(),
|
||||
status: resourceStatus,
|
||||
}
|
||||
if rr.mr.Cluster == "" {
|
||||
rr.mr.Cluster = multicluster.ClusterLocalName
|
||||
}
|
||||
return rr
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
)
|
||||
|
||||
func TestResourceTreePrintOption_getWidthForDetails(t *testing.T) {
|
||||
@@ -46,3 +50,39 @@ func TestResourceTreePrintOptions_wrapDetails(t *testing.T) {
|
||||
},
|
||||
options._wrapDetails(detail, 40))
|
||||
}
|
||||
|
||||
func TestBuildResourceRow(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
cases := map[string]struct {
|
||||
Cluster string
|
||||
ResourceRowStatus string
|
||||
ExpectedCluster string
|
||||
ExpectedResourceRowStatus string
|
||||
}{
|
||||
"localCluster": {
|
||||
Cluster: "",
|
||||
ResourceRowStatus: resourceRowStatusUpdated,
|
||||
ExpectedCluster: multicluster.ClusterLocalName,
|
||||
ExpectedResourceRowStatus: resourceRowStatusUpdated,
|
||||
},
|
||||
"remoteCluster": {
|
||||
Cluster: "remoteCluster",
|
||||
ResourceRowStatus: resourceRowStatusUpdated,
|
||||
ExpectedCluster: "remoteCluster",
|
||||
ExpectedResourceRowStatus: resourceRowStatusUpdated,
|
||||
},
|
||||
}
|
||||
|
||||
for name, c := range cases {
|
||||
mr := v1beta1.ManagedResource{
|
||||
ClusterObjectReference: common.ClusterObjectReference{
|
||||
Cluster: c.Cluster,
|
||||
},
|
||||
}
|
||||
rr := buildResourceRow(mr, c.ResourceRowStatus)
|
||||
r.Equal(c.ExpectedCluster, rr.mr.Cluster, name)
|
||||
r.Equal(c.ExpectedResourceRowStatus, rr.status, name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,20 +19,32 @@ package stdlib
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"cuelang.org/go/cue/build"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
BuiltinImports, err = initBuiltinImports()
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Unable to init builtin imports")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
//go:embed pkgs op.cue ql.cue
|
||||
fs embed.FS
|
||||
// BuiltinImports is the builtin imports for cue
|
||||
BuiltinImports []*build.Instance
|
||||
)
|
||||
|
||||
// GetPackages Get Stdlib packages
|
||||
func GetPackages(tagTempl string) (map[string]string, error) {
|
||||
|
||||
func GetPackages() (map[string]string, error) {
|
||||
files, err := fs.ReadDir("pkgs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -63,16 +75,32 @@ func GetPackages(tagTempl string) (map[string]string, error) {
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
"vela/op": opContent + "\n" + tagTempl,
|
||||
"vela/ql": qlContent + "\n" + tagTempl,
|
||||
"vela/op": opContent,
|
||||
"vela/ql": qlContent,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AddImportsFor install imports for build.Instance.
|
||||
func AddImportsFor(inst *build.Instance, tagTempl string) error {
|
||||
pkgs, err := GetPackages(tagTempl)
|
||||
inst.Imports = append(inst.Imports, BuiltinImports...)
|
||||
if tagTempl != "" {
|
||||
p := &build.Instance{
|
||||
PkgName: filepath.Base("vela/custom"),
|
||||
ImportPath: "vela/custom",
|
||||
}
|
||||
if err := p.AddFile("-", tagTempl); err != nil {
|
||||
return err
|
||||
}
|
||||
inst.Imports = append(inst.Imports, p)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initBuiltinImports() ([]*build.Instance, error) {
|
||||
imports := make([]*build.Instance, 0)
|
||||
pkgs, err := GetPackages()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
for path, content := range pkgs {
|
||||
p := &build.Instance{
|
||||
@@ -80,9 +108,9 @@ func AddImportsFor(inst *build.Instance, tagTempl string) error {
|
||||
ImportPath: path,
|
||||
}
|
||||
if err := p.AddFile("-", content); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
inst.Imports = append(inst.Imports, p)
|
||||
imports = append(imports, p)
|
||||
}
|
||||
return nil
|
||||
return imports, nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
)
|
||||
|
||||
func TestGetPackages(t *testing.T) {
|
||||
pkgs, err := GetPackages("context: _")
|
||||
pkgs, err := GetPackages()
|
||||
assert.NilError(t, err)
|
||||
var r cue.Runtime
|
||||
for path, content := range pkgs {
|
||||
@@ -36,8 +36,8 @@ func TestGetPackages(t *testing.T) {
|
||||
|
||||
builder := &build.Instance{}
|
||||
builder.AddFile("-", `
|
||||
import "vela/op"
|
||||
out: op.context`)
|
||||
import "vela/custom"
|
||||
out: custom.context`)
|
||||
err = AddImportsFor(builder, "context: id: \"xxx\"")
|
||||
assert.NilError(t, err)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
kind: string
|
||||
}
|
||||
filter?: {
|
||||
namespace?: *"" | string
|
||||
namespace?: string
|
||||
matchingLabels?: {...}
|
||||
}
|
||||
list?: {...}
|
||||
|
||||
@@ -86,9 +86,9 @@
|
||||
limitBytes: *null | int
|
||||
}
|
||||
outputs?: {
|
||||
logs: string
|
||||
err?: string
|
||||
info: {
|
||||
logs?: string
|
||||
err?: string
|
||||
info?: {
|
||||
fromDate: string
|
||||
toDate: string
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
errAuthenticateProvider = "failed to authenticate Terraform cloud provider %s for %s"
|
||||
errProviderExists = "terraform provider %s for %s already exists"
|
||||
errDeleteProvider = "failed to delete Terraform Provider %s err: %w"
|
||||
errCouldNotDeleteProvider = "the Terraform Provider %s could not be disabled because it was created by enabling a Terraform provider or was manually created"
|
||||
@@ -54,7 +53,7 @@ func CreateApplication(ctx context.Context, k8sClient client.Client, name, compo
|
||||
if strings.HasPrefix(componentType, types.TerraformComponentPrefix) {
|
||||
existed, err := IsTerraformProviderExisted(ctx, k8sClient, name)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, errAuthenticateProvider, name, componentType)
|
||||
return errors.Wrapf(err, errCheckProviderExistence, name)
|
||||
}
|
||||
if existed {
|
||||
return fmt.Errorf(errProviderExists, name, componentType)
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
)
|
||||
|
||||
// Meta is the struct for image metadata
|
||||
type Meta struct {
|
||||
Registry string
|
||||
Repository string
|
||||
Name string
|
||||
Tag string
|
||||
}
|
||||
|
||||
// DockerHubImageTagResponse is the struct for docker hub image tag response
|
||||
type DockerHubImageTagResponse struct {
|
||||
Count int `json:"count"`
|
||||
Results []Result
|
||||
}
|
||||
|
||||
// Result is the struct for docker hub image tag result
|
||||
type Result struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// IsExisted checks whether a public or private image exists
|
||||
func IsExisted(username, password, image string) (bool, error) {
|
||||
ref, err := name.ParseReference(image)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var img v1.Image
|
||||
|
||||
if username != "" || password != "" {
|
||||
basic := &authn.Basic{
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
option := remote.WithAuth(basic)
|
||||
img, err = remote.Image(ref, option)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
img, err = remote.Image(ref)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = img.Digest()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
_, err = img.Manifest()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// RegistryMeta is the struct for registry metadata
|
||||
type RegistryMeta struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestExisted(t *testing.T) {
|
||||
type args struct {
|
||||
username string
|
||||
password string
|
||||
image string
|
||||
}
|
||||
type want struct {
|
||||
existed bool
|
||||
errMsg string
|
||||
}
|
||||
testcases := map[string]struct {
|
||||
args args
|
||||
want want
|
||||
}{
|
||||
"empty image name": {
|
||||
args: args{
|
||||
image: "",
|
||||
},
|
||||
want: want{
|
||||
existed: false,
|
||||
errMsg: "could not parse referenc",
|
||||
},
|
||||
},
|
||||
"just image name": {
|
||||
args: args{
|
||||
image: "nginx",
|
||||
},
|
||||
want: want{
|
||||
existed: true,
|
||||
},
|
||||
},
|
||||
" image name with registry": {
|
||||
args: args{
|
||||
image: "docker.io/nginx",
|
||||
},
|
||||
want: want{
|
||||
existed: true,
|
||||
},
|
||||
},
|
||||
"image name with tag": {
|
||||
args: args{
|
||||
image: "nginx:latest",
|
||||
},
|
||||
want: want{
|
||||
existed: true,
|
||||
},
|
||||
},
|
||||
"image name with repository": {
|
||||
args: args{
|
||||
image: "library/nginx:latest",
|
||||
},
|
||||
want: want{
|
||||
existed: true,
|
||||
},
|
||||
},
|
||||
"image name with registry and repository": {
|
||||
args: args{
|
||||
image: "docker.io/library/nginx",
|
||||
},
|
||||
want: want{
|
||||
existed: true,
|
||||
},
|
||||
},
|
||||
"invalid image name": {
|
||||
args: args{
|
||||
image: "jfsdfjwfjwf:fwefsfsjflwejfwjfoewfsffsfw",
|
||||
},
|
||||
want: want{
|
||||
existed: false,
|
||||
errMsg: "UNAUTHORIZED",
|
||||
},
|
||||
},
|
||||
"invalid image registry": {
|
||||
args: args{
|
||||
image: "nginx1sf/jfsdfjwfjwf:fwefsfsjflwejfwjfoewfsffsfw",
|
||||
},
|
||||
want: want{
|
||||
existed: false,
|
||||
errMsg: "UNAUTHORIZED",
|
||||
},
|
||||
},
|
||||
"not docker hub registry": {
|
||||
args: args{
|
||||
image: "alibabacloud.com/d/e:v0.1",
|
||||
},
|
||||
want: want{
|
||||
existed: false,
|
||||
errMsg: "invalid character '<' looking for beginning of value",
|
||||
},
|
||||
},
|
||||
"private registry, invalid authentication": {
|
||||
args: args{
|
||||
image: "abcfefsfjflsfjweffwe73rr.com/d/e:v0.1",
|
||||
username: "admin",
|
||||
},
|
||||
want: want{
|
||||
existed: false,
|
||||
errMsg: "Get",
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got, err := IsExisted(tc.args.username, tc.args.password, tc.args.image)
|
||||
if err != nil || tc.want.errMsg != "" {
|
||||
assert.Contains(t, err.Error(), tc.want.errMsg)
|
||||
}
|
||||
assert.Equal(t, got, tc.want.existed)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
||||
"github.com/oam-dev/kubevela/references/common"
|
||||
)
|
||||
|
||||
// QueryView contains query data
|
||||
@@ -40,6 +43,8 @@ const (
|
||||
KeyWordView = "view"
|
||||
// KeyWordParameter represent parameter keyword
|
||||
KeyWordParameter = "parameter"
|
||||
// KeyWordTemplate represents template keyword
|
||||
KeyWordTemplate = "template"
|
||||
// KeyWordExport represent export keyword
|
||||
KeyWordExport = "export"
|
||||
// DefaultExportValue is the default Export value
|
||||
@@ -91,6 +96,36 @@ func ParseVelaQL(ql string) (QueryView, error) {
|
||||
return qv, nil
|
||||
}
|
||||
|
||||
// ParseVelaQLFromPath will parse a velaQL file path to QueryView
|
||||
func ParseVelaQLFromPath(velaQLViewPath string) (*QueryView, error) {
|
||||
body, err := common.ReadRemoteOrLocalPath(velaQLViewPath)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("read view file from %s: %v", velaQLViewPath, err)
|
||||
}
|
||||
|
||||
val, err := value.NewValue(string(body), nil, "")
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("new value for view: %v", err)
|
||||
}
|
||||
|
||||
var expStr string
|
||||
exp, err := val.LookupValue(KeyWordExport)
|
||||
if err == nil {
|
||||
expStr, err = exp.String()
|
||||
if err != nil {
|
||||
expStr = DefaultExportValue
|
||||
}
|
||||
} else {
|
||||
expStr = DefaultExportValue
|
||||
}
|
||||
|
||||
return &QueryView{
|
||||
View: string(body),
|
||||
Parameter: nil,
|
||||
Export: strings.Trim(strings.TrimSpace(expStr), `"`),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParseParameter parse parameter to map[string]interface{}
|
||||
func ParseParameter(parameter string) (map[string]interface{}, error) {
|
||||
parameter = strings.TrimLeft(parameter, "{")
|
||||
|
||||
@@ -18,12 +18,12 @@ package query
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -397,14 +397,6 @@ func (h *provider) GeneratorServiceEndpoints(wfctx wfContext.Context, v *value.V
|
||||
return fillQueryResult(v, serviceEndpoints, "list")
|
||||
}
|
||||
|
||||
var (
|
||||
terminatedContainerNotFoundRegex = regexp.MustCompile("previous terminated container .+ in pod .+ not found")
|
||||
)
|
||||
|
||||
func isTerminatedContainerNotFound(err error) bool {
|
||||
return err != nil && terminatedContainerNotFoundRegex.MatchString(err.Error())
|
||||
}
|
||||
|
||||
func (h *provider) CollectLogsInPod(ctx wfContext.Context, v *value.Value, act types.Action) error {
|
||||
cluster, err := v.GetString("cluster")
|
||||
if err != nil {
|
||||
@@ -429,27 +421,29 @@ func (h *provider) CollectLogsInPod(ctx wfContext.Context, v *value.Value, act t
|
||||
cliCtx := multicluster.ContextWithClusterName(context.Background(), cluster)
|
||||
clientSet, err := kubernetes.NewForConfig(h.cfg)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create kubernetes clientset")
|
||||
return errors.Wrapf(err, "failed to create kubernetes client")
|
||||
}
|
||||
var defaultOutputs = make(map[string]interface{})
|
||||
var errMsg string
|
||||
podInst, err := clientSet.CoreV1().Pods(namespace).Get(cliCtx, pod, v1.GetOptions{})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get pod")
|
||||
errMsg = fmt.Sprintf("failed to get pod:%s", err.Error())
|
||||
}
|
||||
req := clientSet.CoreV1().Pods(namespace).GetLogs(pod, opts)
|
||||
readCloser, err := req.Stream(cliCtx)
|
||||
if err != nil && !isTerminatedContainerNotFound(err) {
|
||||
return errors.Wrapf(err, "failed to get stream logs")
|
||||
if err != nil {
|
||||
errMsg = fmt.Sprintf("failed to get stream logs %s", err.Error())
|
||||
}
|
||||
r := bufio.NewReader(readCloser)
|
||||
var b strings.Builder
|
||||
var readErr error
|
||||
if err == nil {
|
||||
if readCloser != nil && podInst != nil {
|
||||
r := bufio.NewReader(readCloser)
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
var readErr error
|
||||
defer func() {
|
||||
_ = readCloser.Close()
|
||||
}()
|
||||
for {
|
||||
s, err := r.ReadString('\n')
|
||||
b.WriteString(s)
|
||||
buffer.WriteString(s)
|
||||
if err != nil {
|
||||
if !errors.Is(err, io.EOF) {
|
||||
readErr = err
|
||||
@@ -457,30 +451,34 @@ func (h *provider) CollectLogsInPod(ctx wfContext.Context, v *value.Value, act t
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
readErr = err
|
||||
toDate := v1.Now()
|
||||
var fromDate v1.Time
|
||||
// nolint
|
||||
if opts.SinceTime != nil {
|
||||
fromDate = *opts.SinceTime
|
||||
} else if opts.SinceSeconds != nil {
|
||||
fromDate = v1.NewTime(toDate.Add(time.Duration(-(*opts.SinceSeconds) * int64(time.Second))))
|
||||
} else {
|
||||
fromDate = podInst.CreationTimestamp
|
||||
}
|
||||
// the cue string can not support the special characters
|
||||
logs := base64.StdEncoding.EncodeToString(buffer.Bytes())
|
||||
defaultOutputs = map[string]interface{}{
|
||||
"logs": logs,
|
||||
"info": map[string]interface{}{
|
||||
"fromDate": fromDate,
|
||||
"toDate": toDate,
|
||||
},
|
||||
}
|
||||
if readErr != nil {
|
||||
errMsg = readErr.Error()
|
||||
}
|
||||
}
|
||||
toDate := v1.Now()
|
||||
var fromDate v1.Time
|
||||
// nolint
|
||||
if opts.SinceTime != nil {
|
||||
fromDate = *opts.SinceTime
|
||||
} else if opts.SinceSeconds != nil {
|
||||
fromDate = v1.NewTime(toDate.Add(time.Duration(-(*opts.SinceSeconds) * int64(time.Second))))
|
||||
} else {
|
||||
fromDate = podInst.CreationTimestamp
|
||||
if errMsg != "" {
|
||||
klog.Warningf(errMsg)
|
||||
defaultOutputs["err"] = errMsg
|
||||
}
|
||||
o := map[string]interface{}{
|
||||
"logs": b.String(),
|
||||
"info": map[string]interface{}{
|
||||
"fromDate": fromDate,
|
||||
"toDate": toDate,
|
||||
},
|
||||
}
|
||||
if readErr != nil {
|
||||
o["err"] = readErr.Error()
|
||||
}
|
||||
return v.FillObject(o, "outputs")
|
||||
return v.FillObject(defaultOutputs, "outputs")
|
||||
}
|
||||
|
||||
// Install register handlers to provider discover.
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v12 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -677,6 +678,10 @@ func iteratorChildResources(ctx context.Context, cluster string, k8sClient clien
|
||||
clusterCTX := multicluster.ContextWithClusterName(ctx, cluster)
|
||||
items, err := listItemByRule(clusterCTX, k8sClient, resource, *parentObject, specifiedFunc, rules.DefaultGenListOptionFunc)
|
||||
if err != nil {
|
||||
if meta.IsNoMatchError(err) || runtime.IsNotRegisteredError(err) {
|
||||
log.Logger.Errorf("error to list subresources: %s err: %v", resource.Kind, err)
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range items {
|
||||
|
||||
@@ -1380,6 +1380,54 @@ var _ = Describe("unit-test to e2e test", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(res.List)).Should(Equal(2))
|
||||
})
|
||||
|
||||
It("Test not exist api don't break whole process", func() {
|
||||
notExistRuleStr := `
|
||||
- parentResourceType:
|
||||
group: apps
|
||||
kind: Deployment
|
||||
childrenResourceType:
|
||||
- apiVersion: v2
|
||||
kind: Pod
|
||||
`
|
||||
notExistParentResourceStr := `
|
||||
- parentResourceType:
|
||||
group: badgroup
|
||||
kind: Deployment
|
||||
childrenResourceType:
|
||||
- apiVersion: v2
|
||||
kind: Pod
|
||||
`
|
||||
Expect(k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
badRuleConfigMap := v1.ConfigMap{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "ConfigMap"},
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: types3.DefaultKubeVelaNS, Name: "bad-rule", Labels: map[string]string{oam.LabelResourceRules: "true"}},
|
||||
Data: map[string]string{relationshipKey: notExistRuleStr},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, &badRuleConfigMap)).Should(BeNil())
|
||||
|
||||
notExistParentConfigMap := v1.ConfigMap{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "ConfigMap"},
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: types3.DefaultKubeVelaNS, Name: "not-exist-parent", Labels: map[string]string{oam.LabelResourceRules: "true"}},
|
||||
Data: map[string]string{relationshipKey: notExistParentResourceStr},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, ¬ExistParentConfigMap)).Should(BeNil())
|
||||
|
||||
prd := provider{cli: k8sClient}
|
||||
opt := `app: {
|
||||
name: "app"
|
||||
namespace: "test-namespace"
|
||||
}`
|
||||
v, err := value.NewValue(opt, nil, "")
|
||||
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(prd.GetApplicationResourceTree(nil, v, nil)).Should(BeNil())
|
||||
type Res struct {
|
||||
List []types.AppliedResource `json:"list"`
|
||||
}
|
||||
var res Res
|
||||
err = v.UnmarshalTo(&res)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(res.List)).Should(Equal(2))
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("test merge globalRules", func() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user