mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-01 17:20:45 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17872f9705 | ||
|
|
e463dbf1fb | ||
|
|
44142c4c70 | ||
|
|
e460638799 | ||
|
|
58ca3a820d | ||
|
|
63b31be38e | ||
|
|
06f10d4026 | ||
|
|
7a2b18b78a | ||
|
|
52ee87df16 | ||
|
|
3177d26fc4 | ||
|
|
3df1776b37 | ||
|
|
a6434c3efa | ||
|
|
766594d625 | ||
|
|
f7bd17edd7 | ||
|
|
5213e54466 | ||
|
|
939556a698 | ||
|
|
c8d89a1856 | ||
|
|
891429c5f5 | ||
|
|
8b6dbd781f | ||
|
|
7916e874c5 | ||
|
|
8f78189a79 | ||
|
|
6d979cfcab | ||
|
|
491127daec | ||
|
|
1829cf4e40 | ||
|
|
b0facbeaab |
69
.github/workflows/apiserver-test.yaml
vendored
69
.github/workflows/apiserver-test.yaml
vendored
@@ -19,9 +19,8 @@ env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
|
||||
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
|
||||
K3D_IMAGE_VERSION: '[\"v1.20\",\"v1.23\"]'
|
||||
K3D_IMAGE_VERSIONS: '[\"v1.20\",\"v1.23\"]'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -48,9 +47,9 @@ jobs:
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||
echo "pushing tag: ${{ github.ref_name }}"
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSIONS }}"
|
||||
echo "::set-output name=matrix::${{ env.K3D_IMAGE_VERSIONS }}"
|
||||
else
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSION }}"
|
||||
echo "::set-output name=matrix::${{ env.K3D_IMAGE_VERSION }}"
|
||||
fi
|
||||
|
||||
apiserver-unit-tests:
|
||||
@@ -111,6 +110,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ${{ fromJson(needs.set-k8s-matrix.outputs.matrix) }}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.k8s-version }}
|
||||
cancel-in-progress: true
|
||||
|
||||
steps:
|
||||
- name: Set up Go
|
||||
@@ -128,35 +130,50 @@ jobs:
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
|
||||
- name: Setup Kind
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
- name: Tear down K3d if exist
|
||||
run: |
|
||||
k3d cluster delete || true
|
||||
k3d cluster delete worker || true
|
||||
|
||||
- name: Calculate K3d args
|
||||
run: |
|
||||
EGRESS_ARG=""
|
||||
if [[ "${{ matrix.k8s-version }}" == v1.23 ]]; then
|
||||
EGRESS_ARG="--k3s-arg --egress-selector-mode=disabled@server:0"
|
||||
fi
|
||||
echo "EGRESS_ARG=${EGRESS_ARG}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup K3d (Hub)
|
||||
uses: nolar/setup-k3d-k3s@v1.0.8
|
||||
with:
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
skipClusterCreation: true
|
||||
version: ${{ matrix.k8s-version }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
k3d-args: ${{ env.EGRESS_ARG }}
|
||||
|
||||
- name: Setup Kind Cluster (Worker)
|
||||
|
||||
- name: Setup K3d (Worker)
|
||||
uses: nolar/setup-k3d-k3s@v1.0.8
|
||||
with:
|
||||
version: ${{ matrix.k8s-version }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
k3d-name: worker
|
||||
k3d-args: --kubeconfig-update-default=false --network=k3d-k3s-default ${{ env.EGRESS_ARG }}
|
||||
|
||||
|
||||
- name: Kind Cluster (Worker)
|
||||
run: |
|
||||
kind delete cluster --name worker
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }} --name worker
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
kind get kubeconfig --name worker --internal > /tmp/worker.kubeconfig
|
||||
kind get kubeconfig --name worker > /tmp/worker.client.kubeconfig
|
||||
internal_ip=$(docker network inspect k3d-k3s-default|jq ".[0].Containers"| jq -r '.[]| select(.Name=="k3d-worker-server-0")|.IPv4Address' | cut -d/ -f1)
|
||||
k3d kubeconfig get worker > /tmp/worker.client.kubeconfig
|
||||
cp /tmp/worker.client.kubeconfig /tmp/worker.kubeconfig
|
||||
sed -i "s/0.0.0.0:[0-9]\+/$internal_ip:6443/" /tmp/worker.kubeconfig
|
||||
|
||||
- name: Setup Kind Cluster (Hub)
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }}
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
- name: Load Image to kind cluster
|
||||
run: make kind-load
|
||||
- name: Load image to k3d cluster
|
||||
run: make image-load
|
||||
|
||||
- name: Cleanup for e2e tests
|
||||
run: |
|
||||
make e2e-cleanup
|
||||
make vela-cli
|
||||
make e2e-cleanup
|
||||
make e2e-setup-core
|
||||
bin/vela addon enable fluxcd
|
||||
timeout 600s bash -c -- 'while true; do kubectl get ns flux-system; if [ $? -eq 0 ] ; then break; else sleep 5; fi;done'
|
||||
|
||||
68
.github/workflows/e2e-multicluster-test.yml
vendored
68
.github/workflows/e2e-multicluster-test.yml
vendored
@@ -17,9 +17,8 @@ env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
|
||||
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
|
||||
K3D_IMAGE_VERSION: '[\"v1.20\",\"v1.23\"]'
|
||||
K3D_IMAGE_VERSIONS: '[\"v1.20\",\"v1.23\"]'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -46,9 +45,9 @@ jobs:
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||
echo "pushing tag: ${{ github.ref_name }}"
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSIONS }}"
|
||||
echo "::set-output name=matrix::${{ env.K3D_IMAGE_VERSIONS }}"
|
||||
else
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSION }}"
|
||||
echo "::set-output name=matrix::${{ env.K3D_IMAGE_VERSION }}"
|
||||
fi
|
||||
|
||||
|
||||
@@ -59,6 +58,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ${{ fromJson(needs.set-k8s-matrix.outputs.matrix) }}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.k8s-version }}
|
||||
cancel-in-progress: true
|
||||
|
||||
|
||||
steps:
|
||||
@@ -74,37 +76,49 @@ jobs:
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
|
||||
- name: Setup Kind
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
- name: Tear down K3d if exist
|
||||
run: |
|
||||
k3d cluster delete || true
|
||||
k3d cluster delete worker || true
|
||||
|
||||
- name: Calculate K3d args
|
||||
run: |
|
||||
EGRESS_ARG=""
|
||||
if [[ "${{ matrix.k8s-version }}" == v1.23 ]]; then
|
||||
EGRESS_ARG="--k3s-arg --egress-selector-mode=disabled@server:0"
|
||||
fi
|
||||
echo "EGRESS_ARG=${EGRESS_ARG}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup K3d (Hub)
|
||||
uses: nolar/setup-k3d-k3s@v1.0.8
|
||||
with:
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
skipClusterCreation: true
|
||||
version: ${{ matrix.k8s-version }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
k3d-args: ${{ env.EGRESS_ARG }}
|
||||
|
||||
- name: Setup Kind Cluster (Worker)
|
||||
- name: Setup K3d (Worker)
|
||||
uses: nolar/setup-k3d-k3s@v1.0.8
|
||||
with:
|
||||
version: ${{ matrix.k8s-version }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
k3d-name: worker
|
||||
k3d-args: --kubeconfig-update-default=false --network=k3d-k3s-default ${{ env.EGRESS_ARG }}
|
||||
|
||||
- name: Generating internal worker kubeconfig
|
||||
run: |
|
||||
kind delete cluster --name worker
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }} --name worker
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
kind get kubeconfig --name worker --internal > /tmp/worker.kubeconfig
|
||||
kind get kubeconfig --name worker > /tmp/worker.client.kubeconfig
|
||||
internal_ip=$(docker network inspect k3d-k3s-default|jq ".[0].Containers"| jq -r '.[]| select(.Name=="k3d-worker-server-0")|.IPv4Address' | cut -d/ -f1)
|
||||
k3d kubeconfig get worker > /tmp/worker.client.kubeconfig
|
||||
cp /tmp/worker.client.kubeconfig /tmp/worker.kubeconfig
|
||||
sed -i "s/0.0.0.0:[0-9]\+/$internal_ip:6443/" /tmp/worker.kubeconfig
|
||||
|
||||
- name: Setup Kind Cluster (Hub)
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }}
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
|
||||
- name: Load Image to kind cluster (Hub)
|
||||
run: make kind-load
|
||||
- name: Load image to k3d cluster (hub and worker)
|
||||
run: make image-load image-load-runtime-cluster
|
||||
|
||||
- name: Cleanup for e2e tests
|
||||
run: |
|
||||
make e2e-cleanup
|
||||
make vela-cli
|
||||
make e2e-cleanup
|
||||
make e2e-setup-core-auth
|
||||
make
|
||||
make setup-runtime-e2e-cluster
|
||||
|
||||
- name: Run e2e multicluster tests
|
||||
|
||||
53
.github/workflows/e2e-rollout-test.yml
vendored
53
.github/workflows/e2e-rollout-test.yml
vendored
@@ -17,9 +17,8 @@ env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
|
||||
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
|
||||
K3D_IMAGE_VERSION: '[\"v1.20\",\"v1.23\"]'
|
||||
K3D_IMAGE_VERSIONS: '[\"v1.20\",\"v1.23\"]'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -46,9 +45,9 @@ jobs:
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||
echo "pushing tag: ${{ github.ref_name }}"
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSIONS }}"
|
||||
echo "::set-output name=matrix::${{ env.K3D_IMAGE_VERSIONS }}"
|
||||
else
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSION }}"
|
||||
echo "::set-output name=matrix::${{ env.K3D_IMAGE_VERSION }}"
|
||||
fi
|
||||
|
||||
e2e-rollout-tests:
|
||||
@@ -58,6 +57,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ${{ fromJson(needs.set-k8s-matrix.outputs.matrix) }}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.k8s-version }}
|
||||
cancel-in-progress: true
|
||||
|
||||
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -72,33 +75,35 @@ jobs:
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
|
||||
- name: Setup Kind
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
skipClusterCreation: true
|
||||
|
||||
- name: Setup Kind Cluster
|
||||
- name: Tear down K3d if exist
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }}
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
k3d cluster delete || true
|
||||
k3d cluster delete worker || true
|
||||
|
||||
- name: Load Image to kind cluster
|
||||
run: make kind-load
|
||||
- name: Calculate K3d args
|
||||
run: |
|
||||
EGRESS_ARG=""
|
||||
if [[ "${{ matrix.k8s-version }}" == v1.23 ]]; then
|
||||
EGRESS_ARG="--k3s-arg --egress-selector-mode=disabled@server:0"
|
||||
fi
|
||||
echo "EGRESS_ARG=${EGRESS_ARG}" >> $GITHUB_ENV
|
||||
|
||||
- name: Run Make
|
||||
run: make
|
||||
- name: Setup K3d
|
||||
uses: nolar/setup-k3d-k3s@v1.0.8
|
||||
with:
|
||||
version: ${{ matrix.k8s-version }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
k3d-args: ${{ env.EGRESS_ARG }}
|
||||
|
||||
- name: Run Make Manager
|
||||
run: make manager
|
||||
- name: Load image to k3d cluster
|
||||
run: make image-load image-load-runtime-cluster
|
||||
|
||||
- name: Prepare for e2e tests
|
||||
run: |
|
||||
make vela-cli
|
||||
make e2e-cleanup
|
||||
make e2e-setup
|
||||
helm lint ./charts/vela-core
|
||||
make e2e-setup-core
|
||||
make setup-runtime-e2e-cluster
|
||||
helm test -n vela-system kubevela --timeout 5m
|
||||
|
||||
- name: Run e2e tests
|
||||
|
||||
52
.github/workflows/e2e-test.yml
vendored
52
.github/workflows/e2e-test.yml
vendored
@@ -17,9 +17,8 @@ env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
KIND_IMAGE_VERSION: '[\"v1.20.7\"]'
|
||||
KIND_IMAGE_VERSIONS: '[\"v1.18.20\",\"v1.20.7\",\"v1.22.7\"]'
|
||||
K3D_IMAGE_VERSION: '[\"v1.20\",\"v1.23\"]'
|
||||
K3D_IMAGE_VERSIONS: '[\"v1.20\",\"v1.23\"]'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -46,9 +45,9 @@ jobs:
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||||
echo "pushing tag: ${{ github.ref_name }}"
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSIONS }}"
|
||||
echo "::set-output name=matrix::${{ env.K3D_IMAGE_VERSIONS }}"
|
||||
else
|
||||
echo "::set-output name=matrix::${{ env.KIND_IMAGE_VERSION }}"
|
||||
echo "::set-output name=matrix::${{ env.K3D_IMAGE_VERSION }}"
|
||||
fi
|
||||
|
||||
e2e-tests:
|
||||
@@ -58,6 +57,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ${{ fromJson(needs.set-k8s-matrix.outputs.matrix) }}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.k8s-version }}
|
||||
cancel-in-progress: true
|
||||
|
||||
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -72,33 +75,36 @@ jobs:
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
|
||||
- name: Setup Kind
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
skipClusterCreation: true
|
||||
|
||||
- name: Setup Kind Cluster
|
||||
- name: Tear down K3d if exist
|
||||
run: |
|
||||
kind delete cluster
|
||||
kind create cluster --image kindest/node:${{ matrix.k8s-version }}
|
||||
kubectl version
|
||||
kubectl cluster-info
|
||||
k3d cluster delete || true
|
||||
k3d cluster delete worker || true
|
||||
|
||||
- name: Load Image to kind cluster
|
||||
run: make kind-load
|
||||
- name: Calculate K3d args
|
||||
run: |
|
||||
EGRESS_ARG=""
|
||||
if [[ "${{ matrix.k8s-version }}" == v1.23 ]]; then
|
||||
EGRESS_ARG="--k3s-arg --egress-selector-mode=disabled@server:0"
|
||||
fi
|
||||
echo "EGRESS_ARG=${EGRESS_ARG}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup K3d
|
||||
uses: nolar/setup-k3d-k3s@v1.0.8
|
||||
with:
|
||||
version: ${{ matrix.k8s-version }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
k3d-args: ${{ env.EGRESS_ARG }}
|
||||
|
||||
- name: Load image to k3d cluster
|
||||
run: make image-load
|
||||
|
||||
- name: Run Make
|
||||
run: make
|
||||
|
||||
- name: Run Make Manager
|
||||
run: make manager
|
||||
|
||||
- name: Prepare for e2e tests
|
||||
run: |
|
||||
make e2e-cleanup
|
||||
make e2e-setup
|
||||
helm lint ./charts/vela-core
|
||||
make e2e-setup-core
|
||||
helm test -n vela-system kubevela --timeout 5m
|
||||
|
||||
- name: Run api e2e tests
|
||||
|
||||
1
.github/workflows/go.yml
vendored
1
.github/workflows/go.yml
vendored
@@ -15,7 +15,6 @@ env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
8
.github/workflows/unit-test.yml
vendored
8
.github/workflows/unit-test.yml
vendored
@@ -15,7 +15,6 @@ env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -61,10 +60,11 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get install -y golang-ginkgo-dev
|
||||
|
||||
- name: Setup Kind Cluster
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
- name: Setup K3d
|
||||
uses: nolar/setup-k3d-k3s@v1.0.8
|
||||
with:
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
version: v1.20
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: install Kubebuilder
|
||||
uses: RyanSiu1995/kubebuilder-action@v1.2
|
||||
|
||||
12
Makefile
12
Makefile
@@ -82,17 +82,17 @@ endif
|
||||
|
||||
|
||||
|
||||
# load docker image to the kind cluster
|
||||
kind-load: kind-load-runtime-cluster
|
||||
# load docker image to the k3d cluster
|
||||
image-load:
|
||||
docker build -t $(VELA_CORE_TEST_IMAGE) -f Dockerfile.e2e .
|
||||
kind load docker-image $(VELA_CORE_TEST_IMAGE) || { echo >&2 "kind not installed or error loading image: $(VELA_CORE_TEST_IMAGE)"; exit 1; }
|
||||
k3d image import $(VELA_CORE_TEST_IMAGE) || { echo >&2 "kind not installed or error loading image: $(VELA_CORE_TEST_IMAGE)"; exit 1; }
|
||||
|
||||
kind-load-runtime-cluster:
|
||||
image-load-runtime-cluster:
|
||||
/bin/sh hack/e2e/build_runtime_rollout.sh
|
||||
docker build -t $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) -f runtime/rollout/e2e/Dockerfile.e2e runtime/rollout/e2e/
|
||||
rm -rf runtime/rollout/e2e/tmp
|
||||
kind load docker-image $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) || { echo >&2 "kind not installed or error loading image: $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE)"; exit 1; }
|
||||
kind load docker-image $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) --name=$(RUNTIME_CLUSTER_NAME) || { echo >&2 "kind not installed or error loading image: $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE)"; exit 1; }
|
||||
k3d image import $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) || { echo >&2 "kind not installed or error loading image: $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE)"; exit 1; }
|
||||
k3d cluster get $(RUNTIME_CLUSTER_NAME) && k3d image import $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) --cluster=$(RUNTIME_CLUSTER_NAME) || echo "no worker cluster"
|
||||
|
||||
# Run tests
|
||||
core-test: fmt vet manifests
|
||||
|
||||
@@ -53,15 +53,7 @@ func (in ApplyOncePolicySpec) FindStrategy(manifest *unstructured.Unstructured)
|
||||
return nil
|
||||
}
|
||||
for _, rule := range in.Rules {
|
||||
match := func(src []string, val string) (found bool) {
|
||||
for _, _val := range src {
|
||||
found = found || _val == val
|
||||
}
|
||||
return val != "" && found
|
||||
}
|
||||
if (match(rule.Selector.CompNames, manifest.GetName()) && match(rule.Selector.ResourceTypes, manifest.GetKind())) ||
|
||||
(rule.Selector.CompNames == nil && match(rule.Selector.ResourceTypes, manifest.GetKind()) ||
|
||||
(rule.Selector.ResourceTypes == nil && match(rule.Selector.CompNames, manifest.GetName()))) {
|
||||
if rule.Selector.Match(manifest) {
|
||||
return rule.Strategy
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/utils/pointer"
|
||||
"k8s.io/utils/strings/slices"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
@@ -57,10 +58,7 @@ type GarbageCollectPolicyRule struct {
|
||||
}
|
||||
|
||||
// ResourcePolicyRuleSelector select the targets of the rule
|
||||
// 1) for GarbageCollectPolicyRule
|
||||
// if both traitTypes, oamTypes and componentTypes are specified, combination logic is OR
|
||||
// if one resource is specified with conflict strategies, strategy as component go first.
|
||||
// 2) for ApplyOncePolicyRule only CompNames and ResourceTypes are used
|
||||
// if multiple conditions are specified, combination logic is AND
|
||||
type ResourcePolicyRuleSelector struct {
|
||||
CompNames []string `json:"componentNames,omitempty"`
|
||||
CompTypes []string `json:"componentTypes,omitempty"`
|
||||
@@ -71,6 +69,8 @@ type ResourcePolicyRuleSelector struct {
|
||||
}
|
||||
|
||||
// Match check if current rule selector match the target resource
|
||||
// If at least one condition is matched and no other condition failed (could be empty), return true
|
||||
// Otherwise, return false
|
||||
func (in *ResourcePolicyRuleSelector) Match(manifest *unstructured.Unstructured) bool {
|
||||
var compName, compType, oamType, traitType, resourceType, resourceName string
|
||||
if labels := manifest.GetLabels(); labels != nil {
|
||||
@@ -81,15 +81,33 @@ func (in *ResourcePolicyRuleSelector) Match(manifest *unstructured.Unstructured)
|
||||
}
|
||||
resourceType = manifest.GetKind()
|
||||
resourceName = manifest.GetName()
|
||||
match := func(src []string, val string) (found bool) {
|
||||
return val != "" && slices.Contains(src, val)
|
||||
match := func(src []string, val string) (found *bool) {
|
||||
if len(src) == 0 {
|
||||
return nil
|
||||
}
|
||||
return pointer.Bool(val != "" && slices.Contains(src, val))
|
||||
}
|
||||
return match(in.CompNames, compName) ||
|
||||
match(in.CompTypes, compType) ||
|
||||
match(in.OAMResourceTypes, oamType) ||
|
||||
match(in.TraitTypes, traitType) ||
|
||||
match(in.ResourceTypes, resourceType) ||
|
||||
match(in.ResourceNames, resourceName)
|
||||
conditions := []*bool{
|
||||
match(in.CompNames, compName),
|
||||
match(in.CompTypes, compType),
|
||||
match(in.OAMResourceTypes, oamType),
|
||||
match(in.TraitTypes, traitType),
|
||||
match(in.ResourceTypes, resourceType),
|
||||
match(in.ResourceNames, resourceName),
|
||||
}
|
||||
hasMatched := false
|
||||
for _, cond := range conditions {
|
||||
// if any non-empty condition failed, return false
|
||||
if cond != nil && !*cond {
|
||||
return false
|
||||
}
|
||||
// if condition succeed, record it
|
||||
if cond != nil && *cond {
|
||||
hasMatched = true
|
||||
}
|
||||
}
|
||||
// if at least one condition is met, return true
|
||||
return hasMatched
|
||||
}
|
||||
|
||||
// GarbageCollectStrategy the strategy for target resource to recycle
|
||||
|
||||
@@ -123,12 +123,13 @@ func (in ManagedResource) NamespacedName() types.NamespacedName {
|
||||
|
||||
// ResourceKey computes the key for managed resource, resources with the same key points to the same resource
|
||||
func (in ManagedResource) ResourceKey() string {
|
||||
gv, kind := in.GroupVersionKind().ToAPIVersionAndKind()
|
||||
group := in.GroupVersionKind().Group
|
||||
kind := in.GroupVersionKind().Kind
|
||||
cluster := in.Cluster
|
||||
if cluster == "" {
|
||||
cluster = velatypes.ClusterLocalName
|
||||
}
|
||||
return strings.Join([]string{gv, kind, cluster, in.Namespace, in.Name}, "/")
|
||||
return strings.Join([]string{group, kind, cluster, in.Namespace, in.Name}, "/")
|
||||
}
|
||||
|
||||
// ComponentKey computes the key for the component which managed resource belongs to
|
||||
|
||||
@@ -125,7 +125,7 @@ func TestManagedResourceKeys(t *testing.T) {
|
||||
},
|
||||
}
|
||||
r.Equal("namespace/name", input.NamespacedName().String())
|
||||
r.Equal("apps/v1/Deployment/cluster/namespace/name", input.ResourceKey())
|
||||
r.Equal("apps/Deployment/cluster/namespace/name", input.ResourceKey())
|
||||
r.Equal("env/component", input.ComponentKey())
|
||||
r.Equal("Deployment name (Cluster: cluster, Namespace: namespace)", input.DisplayName())
|
||||
var deploy1, deploy2 v12.Deployment
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add annotations on your workload. if it generates pod, add same annotations for generated pods.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: annotations
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add command on K8s pod for your workload which follows the pod spec in path 'spec.template'
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: command
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Set the image of the container.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: container-image
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Automatically scale the component based on CPU usage.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: cpuscaler
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add env on K8s pod for your workload which follows the pod spec in path 'spec.template'
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: env
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Expose port to enable web traffic for your component.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: expose
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add host aliases on K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: hostalias
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/import-grafana-dashboard.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Import dashboards to Grafana
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: import-grafana-dashboard
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
appliesToWorkloads: []
|
||||
conflictsWith: []
|
||||
podDisruptive: false
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: registerdatasource: {
|
||||
apiVersion: "grafana.extension.oam.dev/v1alpha1"
|
||||
kind: "ImportDashboard"
|
||||
spec: {
|
||||
grafana: {
|
||||
service: parameter.grafanaServiceName
|
||||
namespace: parameter.grafanaServiceNamespace
|
||||
credentialSecret: parameter.credentialSecret
|
||||
credentialSecretNamespace: parameter.credentialSecretNamespace
|
||||
}
|
||||
urls: parameter.urls
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
grafanaServiceName: string
|
||||
grafanaServiceNamespace: *"default" | string
|
||||
credentialSecret: string
|
||||
credentialSecretNamespace: *"default" | string
|
||||
urls: [...string]
|
||||
|
||||
}
|
||||
workloadRefPath: ""
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: add an init container and use shared volume with pod
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: init-container
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add labels on your workload. if it generates pod, add same label for generated pods.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: labels
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add lifecycle hooks for every container of K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: lifecycle
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/register-grafana-datasource.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add a datasource to Grafana
|
||||
labels:
|
||||
custom.definition.oam.dev/deprecated: "true"
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: register-grafana-datasource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
appliesToWorkloads: []
|
||||
conflictsWith: []
|
||||
podDisruptive: false
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: registerdatasource: {
|
||||
apiVersion: "grafana.extension.oam.dev/v1alpha1"
|
||||
kind: "DatasourceRegistration"
|
||||
spec: {
|
||||
grafana: {
|
||||
service: parameter.grafanaServiceName
|
||||
namespace: parameter.grafanaServiceNamespace
|
||||
credentialSecret: parameter.credentialSecret
|
||||
credentialSecretNamespace: parameter.credentialSecretNamespace
|
||||
}
|
||||
datasource: {
|
||||
name: parameter.name
|
||||
type: parameter.type
|
||||
access: parameter.access
|
||||
service: parameter.service
|
||||
namespace: parameter.namespace
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
grafanaServiceName: string
|
||||
grafanaServiceNamespace: *"default" | string
|
||||
credentialSecret: string
|
||||
credentialSecretNamespace: string
|
||||
name: string
|
||||
type: string
|
||||
access: *"proxy" | string
|
||||
service: string
|
||||
namespace: *"default" | string
|
||||
}
|
||||
workloadRefPath: ""
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add resource requests and limits on K8s pod for your workload which follows the pod spec in path 'spec.template.'
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/shared-resource.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Configure the resources to be sharable across applications.
|
||||
name: shared-resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
#SharedResourcePolicyRule: {
|
||||
// +usage=Specify how to select the targets of the rule
|
||||
selector: [...#ResourcePolicyRuleSelector]
|
||||
}
|
||||
#ResourcePolicyRuleSelector: {
|
||||
// +usage=Select resources by component names
|
||||
componentNames?: [...string]
|
||||
// +usage=Select resources by component types
|
||||
componentTypes?: [...string]
|
||||
// +usage=Select resources by oamTypes (COMPONENT or TRAIT)
|
||||
oamTypes?: [...string]
|
||||
// +usage=Select resources by trait types
|
||||
traitTypes?: [...string]
|
||||
// +usage=Select resources by resource types (like Deployment)
|
||||
resourceTypes?: [...string]
|
||||
// +usage=Select resources by their names
|
||||
resourceNames?: [...string]
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the list of rules to control shared-resource strategy at resource level.
|
||||
// The selected resource will be sharable across applications. (That means multiple applications
|
||||
// can all read it without conflict, but only the first one can write it)
|
||||
rules?: [...#SharedResourcePolicyRule]
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Inject a sidecar container to K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: sidecar
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -13,7 +13,7 @@ spec:
|
||||
template: |
|
||||
parameter: {
|
||||
// +usage=Specify the names of the clusters to select.
|
||||
cluster?: [...string]
|
||||
clusters?: [...string]
|
||||
// +usage=Specify the label selector for clusters
|
||||
clusterLabelSelector?: [string]: string
|
||||
// +usage=Deprecated: Use clusterLabelSelector instead.
|
||||
|
||||
@@ -13,12 +13,6 @@ spec:
|
||||
properties:
|
||||
image: {{ .Values.imageRegistry }}{{ .Values.test.app.repository }}:{{ .Values.test.app.tag }}
|
||||
port: 8000
|
||||
traits:
|
||||
- type: ingress
|
||||
properties:
|
||||
domain: testsvc.example.com
|
||||
http:
|
||||
"/": 8000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
@@ -52,13 +46,5 @@ spec:
|
||||
kubectl -n {{ include "systemDefinitionNamespace" . }} wait --for=condition=available deployments helm-test-express-server --timeout 3m
|
||||
echo "deployment being available"
|
||||
|
||||
# wait for ingress being created
|
||||
while ! [ `kubectl -n {{ include "systemDefinitionNamespace" . }} get ing helm-test-express-server | grep -v NAME | wc -l` = 1 ]; do
|
||||
echo "waiting for ingress being created"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
|
||||
|
||||
echo "Application and its components are created"
|
||||
restartPolicy: Never
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add annotations on your workload. if it generates pod, add same annotations for generated pods.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: annotations
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add command on K8s pod for your workload which follows the pod spec in path 'spec.template'
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: command
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Set the image of the container.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: container-image
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Automatically scale the component based on CPU usage.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: cpuscaler
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add env on K8s pod for your workload which follows the pod spec in path 'spec.template'
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: env
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Expose port to enable web traffic for your component.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: expose
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add host aliases on K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: hostalias
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: add an init container and use shared volume with pod
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: init-container
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add labels on your workload. if it generates pod, add same label for generated pods.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: labels
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add lifecycle hooks for every container of K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: lifecycle
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Add resource requests and limits on K8s pod for your workload which follows the pod spec in path 'spec.template.'
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/shared-resource.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: PolicyDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Configure the resources to be sharable across applications.
|
||||
name: shared-resource
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
#SharedResourcePolicyRule: {
|
||||
// +usage=Specify how to select the targets of the rule
|
||||
selector: [...#ResourcePolicyRuleSelector]
|
||||
}
|
||||
#ResourcePolicyRuleSelector: {
|
||||
// +usage=Select resources by component names
|
||||
componentNames?: [...string]
|
||||
// +usage=Select resources by component types
|
||||
componentTypes?: [...string]
|
||||
// +usage=Select resources by oamTypes (COMPONENT or TRAIT)
|
||||
oamTypes?: [...string]
|
||||
// +usage=Select resources by trait types
|
||||
traitTypes?: [...string]
|
||||
// +usage=Select resources by resource types (like Deployment)
|
||||
resourceTypes?: [...string]
|
||||
// +usage=Select resources by their names
|
||||
resourceNames?: [...string]
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the list of rules to control shared-resource strategy at resource level.
|
||||
// The selected resource will be sharable across applications. (That means multiple applications
|
||||
// can all read it without conflict, but only the first one can write it)
|
||||
rules?: [...#SharedResourcePolicyRule]
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Inject a sidecar container to K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: sidecar
|
||||
namespace: {{ include "systemDefinitionNamespace" . }}
|
||||
spec:
|
||||
|
||||
@@ -13,7 +13,7 @@ spec:
|
||||
template: |
|
||||
parameter: {
|
||||
// +usage=Specify the names of the clusters to select.
|
||||
cluster?: [...string]
|
||||
clusters?: [...string]
|
||||
// +usage=Specify the label selector for clusters
|
||||
clusterLabelSelector?: [string]: string
|
||||
// +usage=Deprecated: Use clusterLabelSelector instead.
|
||||
|
||||
@@ -19,6 +19,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
goflag "flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -139,6 +140,7 @@ func main() {
|
||||
flag.DurationVar(&clusterMetricsInterval, "cluster-metrics-interval", 15*time.Second, "The interval that ClusterMetricsMgr will collect metrics from clusters, default value is 15 seconds.")
|
||||
flag.BoolVar(&controllerArgs.EnableCompatibility, "enable-asi-compatibility", false, "enable compatibility for asi")
|
||||
flag.BoolVar(&controllerArgs.IgnoreAppWithoutControllerRequirement, "ignore-app-without-controller-version", false, "If true, application controller will not process the app without 'app.oam.dev/controller-version-require' annotation")
|
||||
flag.BoolVar(&controllerArgs.IgnoreDefinitionWithoutControllerRequirement, "ignore-definition-without-controller-version", false, "If true, trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation")
|
||||
standardcontroller.AddOptimizeFlags()
|
||||
standardcontroller.AddAdmissionFlags()
|
||||
flag.IntVar(&resourcekeeper.MaxDispatchConcurrent, "max-dispatch-concurrent", 10, "Set the max dispatch concurrent number, default is 10")
|
||||
@@ -147,9 +149,10 @@ func main() {
|
||||
flag.IntVar(&wfTypes.MaxWorkflowStepErrorRetryTimes, "max-workflow-step-error-retry-times", 10, "Set the max workflow step error retry times, default is 10")
|
||||
utilfeature.DefaultMutableFeatureGate.AddFlag(flag.CommandLine)
|
||||
|
||||
flag.Parse()
|
||||
// setup logging
|
||||
klog.InitFlags(nil)
|
||||
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
|
||||
flag.Parse()
|
||||
if logDebug {
|
||||
_ = flag.Set("v", strconv.Itoa(int(commonconfig.LogDebug)))
|
||||
}
|
||||
|
||||
@@ -76,17 +76,19 @@ func ApplyMockServerConfig() error {
|
||||
} else {
|
||||
cm.ResourceVersion = originCm.ResourceVersion
|
||||
if err = k8sClient.Update(ctx, &cm); err != nil {
|
||||
fmt.Println("print errr------")
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-vela"}}); err != nil {
|
||||
return err
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
otherRegistry.SetNamespace("test-vela")
|
||||
if err := k8sClient.Create(ctx, otherRegistry); err != nil {
|
||||
return err
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ var (
|
||||
func main() {
|
||||
err := utils.ApplyMockServerConfig()
|
||||
if err != nil {
|
||||
log.Fatal("Apply mock server config to ConfigMap fail")
|
||||
log.Fatal(err)
|
||||
}
|
||||
http.HandleFunc("/", ossHandler)
|
||||
http.HandleFunc("/helm/", helmHandler)
|
||||
|
||||
@@ -294,7 +294,7 @@ var VelaQLPodListContext = func(context string, velaQL string) bool {
|
||||
gomega.Expect(v.Status.Phase).To(gomega.ContainSubstring("Running"))
|
||||
}
|
||||
if v.Status.NodeName != "" {
|
||||
gomega.Expect(v.Status.NodeName).To(gomega.ContainSubstring("kind-control-plane"))
|
||||
gomega.Expect(v.Status.NodeName).To(gomega.ContainSubstring("k3d-k3s-default-server-0"))
|
||||
}
|
||||
if v.Metadata.Namespace != "" {
|
||||
gomega.Expect(v.Metadata.Namespace).To(gomega.ContainSubstring("default"))
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/oam-dev/kubevela/e2e"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = ginkgo.BeforeSuite(func() {
|
||||
e2e.BeforeSuit()
|
||||
}, 30)
|
||||
|
||||
func TestApplication(t *testing.T) {
|
||||
gomega.RegisterFailHandler(ginkgo.Fail)
|
||||
ginkgo.RunSpecs(t, "Setup Suite")
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = ginkgo.Describe("Setup", func() {
|
||||
|
||||
})
|
||||
@@ -1,20 +1,20 @@
|
||||
. ./hack/e2e/end_e2e_core.sh
|
||||
|
||||
OAM_CONTAINER_ID=$(docker exec kind-control-plane crictl ps | grep oam-runtime | grep --regexp '^.............' -o)
|
||||
OAM_DOCKER_DIR=$(docker exec kind-control-plane crictl inspect --output go-template --template '{{range .info.runtimeSpec.mounts}}{{if (eq .destination "/workspace/data")}}{{.source}}{{end}}{{end}}' "${OAM_CONTAINER_ID}")
|
||||
OAM_CONTAINER_ID=$(docker exec k3d-k3s-default-server-0 crictl ps | grep oam-runtime | grep --regexp '^.............' -o)
|
||||
OAM_DOCKER_DIR=$(docker exec k3d-k3s-default-server-0 crictl inspect --output go-template --template '{{range .info.runtimeSpec.mounts}}{{if (eq .destination "/workspace/data")}}{{.source}}{{end}}{{end}}' "${OAM_CONTAINER_ID}")
|
||||
echo "${OAM_CONTAINER_ID}"
|
||||
echo "${OAM_DOCKER_DIR}"
|
||||
|
||||
docker exec kind-control-plane crictl exec "${OAM_CONTAINER_ID}" kill -2 1
|
||||
docker exec k3d-k3s-default-server-0 crictl exec "${OAM_CONTAINER_ID}" kill -2 1
|
||||
|
||||
file=$OAM_DOCKER_DIR/e2e-profile.out
|
||||
echo "$file"
|
||||
n=1
|
||||
while [ $n -le 60 ];do
|
||||
if_exist=$(docker exec kind-control-plane sh -c "test -f $file && echo 'ok'")
|
||||
if_exist=$(docker exec k3d-k3s-default-server-0 sh -c "test -f $file && echo 'ok'")
|
||||
echo "$if_exist"
|
||||
if [ -n "$if_exist" ];then
|
||||
docker exec kind-control-plane cat "$file" > /tmp/oam-e2e-profile.out
|
||||
docker exec k3d-k3s-default-server-0 cat "$file" > /tmp/oam-e2e-profile.out
|
||||
break
|
||||
fi
|
||||
echo file not generated yet
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
CONTAINER_ID=$(docker exec kind-control-plane crictl ps | grep 'kubevela\s' | grep --regexp '^.............' -o)
|
||||
DOCKER_DIR=$(docker exec kind-control-plane crictl inspect --output go-template --template '{{range .info.runtimeSpec.mounts}}{{if (eq .destination "/workspace/data")}}{{.source}}{{end}}{{end}}' "${CONTAINER_ID}")
|
||||
CONTAINER_ID=$(docker exec k3d-k3s-default-server-0 crictl ps | grep 'kubevela\s' | grep --regexp '^.............' -o)
|
||||
DOCKER_DIR=$(docker exec k3d-k3s-default-server-0 crictl inspect --output go-template --template '{{range .info.runtimeSpec.mounts}}{{if (eq .destination "/workspace/data")}}{{.source}}{{end}}{{end}}' "${CONTAINER_ID}")
|
||||
echo "${CONTAINER_ID}"
|
||||
echo "${DOCKER_DIR}"
|
||||
|
||||
docker exec kind-control-plane crictl exec "${CONTAINER_ID}" kill -2 1
|
||||
docker exec k3d-k3s-default-server-0 crictl exec "${CONTAINER_ID}" kill -2 1
|
||||
|
||||
file=$DOCKER_DIR/e2e-profile.out
|
||||
echo "$file"
|
||||
n=1
|
||||
while [ $n -le 60 ];do
|
||||
if_exist=$(docker exec kind-control-plane sh -c "test -f $file && echo 'ok'")
|
||||
if_exist=$(docker exec k3d-k3s-default-server-0 sh -c "test -f $file && echo 'ok'")
|
||||
echo "$if_exist"
|
||||
if [ -n "$if_exist" ];then
|
||||
docker exec kind-control-plane cat "$file" > /tmp/e2e-profile.out
|
||||
docker exec k3d-k3s-default-server-0 cat "$file" > /tmp/e2e-profile.out
|
||||
break
|
||||
fi
|
||||
echo file not generated yet
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
template: {
|
||||
patch: spec: template: spec: {
|
||||
tolerations: [{
|
||||
key: "node.kubernetes.io/network-unavailable"
|
||||
key: "node.kubernetes.io/network-unavailable"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
effect: "NoSchedule"
|
||||
}]
|
||||
}
|
||||
parameter: {}
|
||||
|
||||
@@ -5,11 +5,18 @@ e2e-setup-core-pre-hook:
|
||||
.PHONY: e2e-setup-core-post-hook
|
||||
e2e-setup-core-post-hook:
|
||||
kubectl wait --for=condition=Available deployment/kubevela-vela-core -n vela-system --timeout=180s
|
||||
helm upgrade --install --namespace vela-system --wait oam-rollout --set image.repository=vela-runtime-rollout-test --set image.tag=$(GIT_COMMIT) ./runtime/rollout/charts
|
||||
helm install kruise https://github.com/openkruise/charts/releases/download/kruise-1.1.0/kruise-1.1.0.tgz --set featureGates="PreDownloadImageForInPlaceUpdate=true" --set daemon.socketLocation=/run/k3s/containerd/
|
||||
go run ./e2e/addon/mock &
|
||||
sleep 15
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/fluxcd
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/rollout
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/terraform
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/terraform-alibaba ALICLOUD_ACCESS_KEY=xxx ALICLOUD_SECRET_KEY=yyy ALICLOUD_REGION=cn-beijing
|
||||
|
||||
timeout 600s bash -c -- 'while true; do kubectl get ns flux-system; if [ $$? -eq 0 ] ; then break; else sleep 5; fi;done'
|
||||
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=vela-core,app.kubernetes.io/instance=kubevela -n vela-system --timeout=600s
|
||||
kubectl wait --for=condition=Ready pod -l app=source-controller -n flux-system --timeout=600s
|
||||
kubectl wait --for=condition=Ready pod -l app=helm-controller -n flux-system --timeout=600s
|
||||
|
||||
.PHONY: e2e-setup-core-wo-auth
|
||||
e2e-setup-core-wo-auth:
|
||||
@@ -27,28 +34,26 @@ e2e-setup-core-auth: e2e-setup-core-pre-hook e2e-setup-core-w-auth e2e-setup-cor
|
||||
|
||||
.PHONY: setup-runtime-e2e-cluster
|
||||
setup-runtime-e2e-cluster:
|
||||
helm upgrade --install --create-namespace --namespace vela-system --kubeconfig=$(RUNTIME_CLUSTER_CONFIG) --set image.pullPolicy=IfNotPresent --set image.repository=vela-runtime-rollout-test --set image.tag=$(GIT_COMMIT) --wait vela-rollout ./runtime/rollout/charts
|
||||
helm upgrade --install \
|
||||
--namespace vela-system \
|
||||
--wait oam-rollout \
|
||||
--set image.repository=vela-runtime-rollout-test \
|
||||
--set image.tag=$(GIT_COMMIT) \
|
||||
./runtime/rollout/charts
|
||||
|
||||
.PHONY: e2e-setup
|
||||
e2e-setup:
|
||||
helm install kruise https://github.com/openkruise/charts/releases/download/kruise-1.1.0/kruise-1.1.0.tgz --set featureGates="PreDownloadImageForInPlaceUpdate=true"
|
||||
sh ./hack/e2e/modify_charts.sh
|
||||
helm upgrade --install --create-namespace --namespace vela-system --set image.pullPolicy=IfNotPresent --set image.repository=vela-core-test --set applicationRevisionLimit=5 --set dependCheckWait=10s --set image.tag=$(GIT_COMMIT) --wait kubevela ./charts/vela-core
|
||||
helm upgrade --install --namespace vela-system --wait oam-rollout --set image.repository=vela-runtime-rollout-test --set image.tag=$(GIT_COMMIT) ./runtime/rollout/charts
|
||||
k3d cluster get $(RUNTIME_CLUSTER_NAME) && \
|
||||
helm upgrade --install \
|
||||
--create-namespace \
|
||||
--namespace vela-system \
|
||||
--kubeconfig=$(RUNTIME_CLUSTER_CONFIG) \
|
||||
--set image.pullPolicy=IfNotPresent \
|
||||
--set image.repository=vela-runtime-rollout-test \
|
||||
--set image.tag=$(GIT_COMMIT) \
|
||||
--wait vela-rollout \
|
||||
./runtime/rollout/charts || \
|
||||
echo "no worker cluster" \
|
||||
|
||||
go run ./e2e/addon/mock &
|
||||
sleep 15
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/fluxcd
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/terraform
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/terraform-alibaba ALICLOUD_ACCESS_KEY=xxx ALICLOUD_SECRET_KEY=yyy ALICLOUD_REGION=cn-beijing
|
||||
bin/vela addon enable ./e2e/addon/mock/testdata/rollout
|
||||
ginkgo version
|
||||
ginkgo -v -r e2e/setup
|
||||
|
||||
timeout 600s bash -c -- 'while true; do kubectl get ns flux-system; if [ $$? -eq 0 ] ; then break; else sleep 5; fi;done'
|
||||
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=vela-core,app.kubernetes.io/instance=kubevela -n vela-system --timeout=600s
|
||||
kubectl wait --for=condition=Ready pod -l app=source-controller -n flux-system --timeout=600s
|
||||
kubectl wait --for=condition=Ready pod -l app=helm-controller -n flux-system --timeout=600s
|
||||
|
||||
.PHONY: e2e-api-test
|
||||
e2e-api-test:
|
||||
|
||||
@@ -994,12 +994,8 @@ func (h *Installer) getAddonMeta() (map[string]SourceMeta, error) {
|
||||
|
||||
// installDependency checks if addon's dependency and install it
|
||||
func (h *Installer) installDependency(addon *InstallPackage) error {
|
||||
var app v1beta1.Application
|
||||
for _, dep := range addon.Dependencies {
|
||||
err := h.cli.Get(h.ctx, client.ObjectKey{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: addonutil.Addon2AppName(dep.Name),
|
||||
}, &app)
|
||||
_, err := FetchAddonRelatedApp(h.ctx, h.cli, dep.Name)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -418,9 +418,12 @@ var _ = Describe("test override defs of addon", func() {
|
||||
u := unstructured.Unstructured{Object: compUnstructured}
|
||||
u.SetAPIVersion(v1beta1.SchemeGroupVersion.String())
|
||||
u.SetKind(v1beta1.ComponentDefinitionKind)
|
||||
u.SetLabels(map[string]string{"testUpdateLabel": "test"})
|
||||
c, err := checkConflictDefs(ctx, k8sClient, []*unstructured.Unstructured{&u}, app.GetName())
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(c)).Should(BeEquivalentTo(1))
|
||||
// guarantee checkConflictDefs won't change source definition
|
||||
Expect(u.GetLabels()["testUpdateLabel"]).Should(BeEquivalentTo("test"))
|
||||
|
||||
u.SetName("rollout")
|
||||
c, err = checkConflictDefs(ctx, k8sClient, []*unstructured.Unstructured{&u}, app.GetName())
|
||||
|
||||
@@ -448,20 +448,21 @@ func isErrorCueRenderPathNotFound(err error, path string) bool {
|
||||
func checkConflictDefs(ctx context.Context, k8sClient client.Client, defs []*unstructured.Unstructured, appName string) (map[string]string, error) {
|
||||
res := map[string]string{}
|
||||
for _, def := range defs {
|
||||
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(def), def)
|
||||
checkDef := def.DeepCopy()
|
||||
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(checkDef), checkDef)
|
||||
if err == nil {
|
||||
owner := metav1.GetControllerOf(def)
|
||||
owner := metav1.GetControllerOf(checkDef)
|
||||
if owner == nil || owner.Kind != v1beta1.ApplicationKind {
|
||||
res[def.GetName()] = fmt.Sprintf("definition: %s already exist and not belong to any addon \n", def.GetName())
|
||||
res[checkDef.GetName()] = fmt.Sprintf("definition: %s already exist and not belong to any addon \n", checkDef.GetName())
|
||||
continue
|
||||
}
|
||||
if owner.Name != appName {
|
||||
// if addon not belong to an addon or addon name is another one, we should put them in result
|
||||
res[def.GetName()] = fmt.Sprintf("definition: %s in this addon already exist in %s \n", def.GetName(), addon.AppName2Addon(appName))
|
||||
res[checkDef.GetName()] = fmt.Sprintf("definition: %s in this addon already exist in %s \n", checkDef.GetName(), addon.AppName2Addon(appName))
|
||||
}
|
||||
}
|
||||
if err != nil && !errors2.IsNotFound(err) {
|
||||
return nil, errors.Wrapf(err, "check definition %s", def.GetName())
|
||||
return nil, errors.Wrapf(err, "check definition %s", checkDef.GetName())
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
@@ -482,10 +483,16 @@ func produceDefConflictError(conflictDefs map[string]string) error {
|
||||
// checkBondComponentExistt will check the ready-to-apply object(def or auxiliary outputs) whether bind to a component
|
||||
// if the target component not exist, return false.
|
||||
func checkBondComponentExist(u unstructured.Unstructured, app v1beta1.Application) bool {
|
||||
comp, existKey := u.GetAnnotations()[oam.AnnotationIgnoreWithoutCompKey]
|
||||
var comp string
|
||||
var existKey bool
|
||||
comp, existKey = u.GetAnnotations()[oam.AnnotationAddonDefinitionBondCompKey]
|
||||
if !existKey {
|
||||
// if an object(def or auxiliary outputs ) binding no components return true
|
||||
return true
|
||||
// this is compatibility logic for deprecated annotation
|
||||
comp, existKey = u.GetAnnotations()[oam.AnnotationIgnoreWithoutCompKey]
|
||||
if !existKey {
|
||||
// if an object(def or auxiliary outputs ) binding no components return true
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, component := range app.Spec.Components {
|
||||
if component.Name == comp {
|
||||
|
||||
@@ -290,10 +290,13 @@ func TestMakeChart(t *testing.T) {
|
||||
|
||||
func TestCheckObjectBindingComponent(t *testing.T) {
|
||||
existingBindingDef := unstructured.Unstructured{}
|
||||
existingBindingDef.SetAnnotations(map[string]string{oam.AnnotationIgnoreWithoutCompKey: "kustomize"})
|
||||
existingBindingDef.SetAnnotations(map[string]string{oam.AnnotationAddonDefinitionBondCompKey: "kustomize"})
|
||||
|
||||
emptyAnnoDef := unstructured.Unstructured{}
|
||||
emptyAnnoDef.SetAnnotations(map[string]string{"test": "onlyForTest"})
|
||||
|
||||
legacyAnnoDef := unstructured.Unstructured{}
|
||||
legacyAnnoDef.SetAnnotations(map[string]string{oam.AnnotationIgnoreWithoutCompKey: "kustomize"})
|
||||
testCases := map[string]struct {
|
||||
object unstructured.Unstructured
|
||||
app v1beta1.Application
|
||||
@@ -311,6 +314,14 @@ func TestCheckObjectBindingComponent(t *testing.T) {
|
||||
"EmptyApp": {object: existingBindingDef,
|
||||
app: v1beta1.Application{Spec: v1beta1.ApplicationSpec{Components: []common.ApplicationComponent{}}},
|
||||
res: false},
|
||||
"LegacyApp": {object: legacyAnnoDef,
|
||||
app: v1beta1.Application{Spec: v1beta1.ApplicationSpec{Components: []common.ApplicationComponent{{Name: "kustomize"}}}},
|
||||
res: true,
|
||||
},
|
||||
"LegacyAppWithoutComp": {object: legacyAnnoDef,
|
||||
app: v1beta1.Application{Spec: v1beta1.ApplicationSpec{Components: []common.ApplicationComponent{{}}}},
|
||||
res: false,
|
||||
},
|
||||
}
|
||||
for _, s := range testCases {
|
||||
result := checkBondComponentExist(s.object, s.app)
|
||||
|
||||
@@ -32,6 +32,7 @@ const (
|
||||
// SystemInfo systemInfo model
|
||||
type SystemInfo struct {
|
||||
BaseModel
|
||||
SignedKey string `json:"signedKey"`
|
||||
InstallID string `json:"installID"`
|
||||
EnableCollection bool `json:"enableCollection"`
|
||||
StatisticInfo StatisticInfo `json:"statisticInfo,omitempty"`
|
||||
|
||||
@@ -58,7 +58,8 @@ const (
|
||||
GrantTypeRefresh = "refresh"
|
||||
)
|
||||
|
||||
var signedKey = ""
|
||||
// signedKey is the signed key of JWT
|
||||
var signedKey string
|
||||
|
||||
// AuthenticationService is the service of authentication
|
||||
type AuthenticationService interface {
|
||||
|
||||
@@ -72,7 +72,7 @@ func (u *configServiceImpl) ListConfigTypes(ctx context.Context, query string) (
|
||||
defs := &v1beta1.ComponentDefinitionList{}
|
||||
if err := u.KubeClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{
|
||||
configCatalog: types.VelaCoreConfig,
|
||||
definition.ConfigCatalog: types.VelaCoreConfig,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -85,13 +85,13 @@ func (u *configServiceImpl) ListConfigTypes(ctx context.Context, query string) (
|
||||
if err := u.KubeClient.List(ctx, defsLegacy, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{
|
||||
// leave here as the legacy format to test the compatibility
|
||||
definition.UserPrefix + configCatalog: types.VelaCoreConfig,
|
||||
definition.UserPrefix + definition.ConfigCatalog: types.VelaCoreConfig,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// filter repeated config,due to new labels that exist at the same time
|
||||
for _, legacy := range defsLegacy.Items {
|
||||
if legacy.Labels[configCatalog] == types.VelaCoreConfig {
|
||||
if legacy.Labels[definition.ConfigCatalog] == types.VelaCoreConfig {
|
||||
continue
|
||||
}
|
||||
items = append(items, legacy)
|
||||
|
||||
@@ -57,8 +57,8 @@ func TestListConfigTypes(t *testing.T) {
|
||||
Name: "def1",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{
|
||||
configCatalog: types.VelaCoreConfig,
|
||||
definitionType: types.TerraformProvider,
|
||||
definition.ConfigCatalog: types.VelaCoreConfig,
|
||||
definition.DefinitionType: types.TerraformProvider,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -71,10 +71,10 @@ func TestListConfigTypes(t *testing.T) {
|
||||
Name: "def2",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Annotations: map[string]string{
|
||||
definitionAlias: "Def2",
|
||||
definition.DefinitionAlias: "Def2",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
configCatalog: types.VelaCoreConfig,
|
||||
definition.ConfigCatalog: types.VelaCoreConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -153,10 +153,10 @@ func TestGetConfigType(t *testing.T) {
|
||||
Name: "def2",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Annotations: map[string]string{
|
||||
definitionAlias: "Def2",
|
||||
definition.DefinitionAlias: "Def2",
|
||||
},
|
||||
Labels: map[string]string{
|
||||
definition.UserPrefix + configCatalog: types.VelaCoreConfig,
|
||||
definition.UserPrefix + definition.ConfigCatalog: types.VelaCoreConfig,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ func (u systemInfoServiceImpl) Get(ctx context.Context) (*model.SystemInfo, erro
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
info.SignedKey = rand.String(32)
|
||||
installID := rand.String(16)
|
||||
info.InstallID = installID
|
||||
info.EnableCollection = true
|
||||
@@ -161,7 +162,7 @@ func (u systemInfoServiceImpl) Init(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signedKey = info.InstallID
|
||||
signedKey = info.SignedKey
|
||||
_, err = initDexConfig(ctx, u.KubeClient, "http://velaux.com")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -18,22 +18,16 @@ package service
|
||||
|
||||
import "github.com/oam-dev/kubevela/pkg/definition"
|
||||
|
||||
const (
|
||||
definitionAlias = "alias.config.oam.dev"
|
||||
definitionType = "type.config.oam.dev"
|
||||
configCatalog = "catalog.config.oam.dev"
|
||||
)
|
||||
|
||||
// DefinitionAlias will get definitionAlias value from tags
|
||||
func DefinitionAlias(tags map[string]string) string {
|
||||
if tags == nil {
|
||||
return ""
|
||||
}
|
||||
val := tags[definitionAlias]
|
||||
val := tags[definition.DefinitionAlias]
|
||||
if val != "" {
|
||||
return val
|
||||
}
|
||||
return tags[definition.UserPrefix+definitionAlias]
|
||||
return tags[definition.UserPrefix+definition.DefinitionAlias]
|
||||
}
|
||||
|
||||
// DefinitionType will get definitionType value from tags
|
||||
@@ -41,11 +35,11 @@ func DefinitionType(tags map[string]string) string {
|
||||
if tags == nil {
|
||||
return ""
|
||||
}
|
||||
val := tags[definitionType]
|
||||
val := tags[definition.DefinitionType]
|
||||
if val != "" {
|
||||
return val
|
||||
}
|
||||
return tags[definition.UserPrefix+definitionType]
|
||||
return tags[definition.UserPrefix+definition.DefinitionType]
|
||||
}
|
||||
|
||||
// ConfigCatalog will get configCatalog value from tags
|
||||
@@ -53,9 +47,9 @@ func ConfigCatalog(tags map[string]string) string {
|
||||
if tags == nil {
|
||||
return ""
|
||||
}
|
||||
val := tags[configCatalog]
|
||||
val := tags[definition.ConfigCatalog]
|
||||
if val != "" {
|
||||
return val
|
||||
}
|
||||
return tags[definition.UserPrefix+configCatalog]
|
||||
return tags[definition.UserPrefix+definition.ConfigCatalog]
|
||||
}
|
||||
|
||||
@@ -19,20 +19,22 @@ package service
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/definition"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCompatiblleTag(t *testing.T) {
|
||||
func TestCompatibleTag(t *testing.T) {
|
||||
tg := map[string]string{
|
||||
"alias.config.oam.dev": "abc",
|
||||
"type.config.oam.dev": "image-registry",
|
||||
"catalog.config.oam.dev": "cata",
|
||||
definition.DefinitionAlias: "abc",
|
||||
definition.DefinitionType: "image-registry",
|
||||
definition.ConfigCatalog: "cata",
|
||||
}
|
||||
|
||||
tgOld := map[string]string{
|
||||
"custom.definition.oam.dev/alias.config.oam.dev": "abc-2",
|
||||
"custom.definition.oam.dev/type.config.oam.dev": "image-registry-2",
|
||||
"custom.definition.oam.dev/catalog.config.oam.dev": "cata-2",
|
||||
definition.UserPrefix + definition.DefinitionAlias: "abc-2",
|
||||
definition.UserPrefix + definition.DefinitionType: "image-registry-2",
|
||||
definition.UserPrefix + definition.ConfigCatalog: "cata-2",
|
||||
}
|
||||
|
||||
assert.Equal(t, DefinitionAlias(nil), "")
|
||||
|
||||
@@ -54,9 +54,11 @@ func StoreProject(ctx context.Context, name string, ds datastore.DataStore, proj
|
||||
|
||||
// StoreAppMeta will sync application metadata from CR to datastore
|
||||
func StoreAppMeta(ctx context.Context, app *model.DataStoreApp, ds datastore.DataStore) error {
|
||||
err := ds.Get(ctx, &model.Application{Name: app.AppMeta.Name})
|
||||
oldApp := &model.Application{Name: app.AppMeta.Name}
|
||||
err := ds.Get(ctx, oldApp)
|
||||
if err == nil {
|
||||
// it means the record already exists
|
||||
app.AppMeta.CreateTime = oldApp.CreateTime
|
||||
return ds.Put(ctx, app.AppMeta)
|
||||
}
|
||||
if !errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
@@ -75,6 +77,7 @@ func StoreEnv(ctx context.Context, app *model.DataStoreApp, ds datastore.DataSto
|
||||
if utils.EqualSlice(curEnv.Targets, app.Env.Targets) {
|
||||
return nil
|
||||
}
|
||||
app.Env.CreateTime = curEnv.CreateTime
|
||||
return ds.Put(ctx, app.Env)
|
||||
}
|
||||
if !errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
@@ -119,9 +122,11 @@ func StoreComponents(ctx context.Context, appPrimaryKey string, expComps []*mode
|
||||
return err
|
||||
}
|
||||
var originCompNames []string
|
||||
for _, entity := range originComps {
|
||||
comp := entity.(*model.ApplicationComponent)
|
||||
var existComponentMap = make(map[string]*model.ApplicationComponent)
|
||||
for i := range originComps {
|
||||
comp := originComps[i].(*model.ApplicationComponent)
|
||||
originCompNames = append(originCompNames, comp.Name)
|
||||
existComponentMap[comp.Name] = comp
|
||||
}
|
||||
|
||||
var targetCompNames []string
|
||||
@@ -154,6 +159,9 @@ func StoreComponents(ctx context.Context, appPrimaryKey string, expComps []*mode
|
||||
if utils.StringsContain(readyToAdd, comp.Name) {
|
||||
err = ds.Add(ctx, comp)
|
||||
} else {
|
||||
if old := existComponentMap[comp.Name]; old != nil {
|
||||
comp.CreateTime = old.CreateTime
|
||||
}
|
||||
err = ds.Put(ctx, comp)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -172,9 +180,11 @@ func StorePolicy(ctx context.Context, appPrimaryKey string, expPolicies []*model
|
||||
return err
|
||||
}
|
||||
var originPolicyNames []string
|
||||
for _, entity := range originPolicies {
|
||||
plc := entity.(*model.ApplicationPolicy)
|
||||
var policyMap = make(map[string]*model.ApplicationPolicy)
|
||||
for i := range originPolicies {
|
||||
plc := originPolicies[i].(*model.ApplicationPolicy)
|
||||
originPolicyNames = append(originPolicyNames, plc.Name)
|
||||
policyMap[plc.Name] = plc
|
||||
}
|
||||
|
||||
var targetPLCNames []string
|
||||
@@ -209,6 +219,9 @@ func StorePolicy(ctx context.Context, appPrimaryKey string, expPolicies []*model
|
||||
if utils.StringsContain(readyToAdd, plc.Name) {
|
||||
err = ds.Add(ctx, plc)
|
||||
} else {
|
||||
if existPolicy := policyMap[plc.Name]; existPolicy != nil {
|
||||
plc.CreateTime = existPolicy.CreateTime
|
||||
}
|
||||
err = ds.Put(ctx, plc)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -221,8 +234,10 @@ func StorePolicy(ctx context.Context, appPrimaryKey string, expPolicies []*model
|
||||
|
||||
// StoreWorkflow will sync workflow application CR to datastore, it updates the only one workflow from the application with specified name
|
||||
func StoreWorkflow(ctx context.Context, dsApp *model.DataStoreApp, ds datastore.DataStore) error {
|
||||
err := ds.Get(ctx, &model.Workflow{AppPrimaryKey: dsApp.AppMeta.Name, Name: dsApp.Workflow.Name})
|
||||
old := &model.Workflow{AppPrimaryKey: dsApp.AppMeta.Name, Name: dsApp.Workflow.Name}
|
||||
err := ds.Get(ctx, old)
|
||||
if err == nil {
|
||||
dsApp.Workflow.CreateTime = old.CreateTime
|
||||
// it means the record already exists, update it
|
||||
return ds.Put(ctx, dsApp.Workflow)
|
||||
}
|
||||
@@ -254,8 +269,10 @@ func StoreApplicationRevision(ctx context.Context, dsApp *model.DataStoreApp, ds
|
||||
if dsApp.Revision == nil {
|
||||
return nil
|
||||
}
|
||||
err := ds.Get(ctx, &model.ApplicationRevision{AppPrimaryKey: dsApp.AppMeta.Name, Version: dsApp.Revision.Version})
|
||||
old := &model.ApplicationRevision{AppPrimaryKey: dsApp.AppMeta.Name, Version: dsApp.Revision.Version}
|
||||
err := ds.Get(ctx, old)
|
||||
if err == nil {
|
||||
dsApp.Revision.CreateTime = old.CreateTime
|
||||
return ds.Put(ctx, dsApp.Revision)
|
||||
}
|
||||
if !errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
|
||||
@@ -88,6 +88,7 @@ var _ = Describe("Test Worker CR sync to datastore", func() {
|
||||
|
||||
comp1 := model.ApplicationComponent{AppPrimaryKey: app1.Name, Name: "nginx"}
|
||||
Expect(ds.Get(ctx, &comp1)).Should(BeNil())
|
||||
Expect(comp1.CreateTime.IsZero()).Should(BeFalse())
|
||||
Expect(comp1.Properties).Should(BeEquivalentTo(&model.JSONStruct{"image": "nginx"}))
|
||||
|
||||
comp2 := model.ApplicationComponent{AppPrimaryKey: app1.Name, Name: "nginx2"}
|
||||
@@ -96,10 +97,12 @@ var _ = Describe("Test Worker CR sync to datastore", func() {
|
||||
|
||||
env := model.Env{Project: appNS1, Name: model.AutoGenEnvNamePrefix + appNS1}
|
||||
Expect(ds.Get(ctx, &env)).Should(BeNil())
|
||||
Expect(env.CreateTime.IsZero()).Should(BeFalse())
|
||||
Expect(len(env.Targets)).Should(Equal(2))
|
||||
|
||||
appPlc1 := model.ApplicationPolicy{AppPrimaryKey: app1.Name, Name: "topology-beijing-demo"}
|
||||
Expect(ds.Get(ctx, &appPlc1)).Should(BeNil())
|
||||
Expect(appPlc1.CreateTime.IsZero()).Should(BeFalse())
|
||||
appPlc2 := model.ApplicationPolicy{AppPrimaryKey: app1.Name, Name: "topology-local"}
|
||||
Expect(ds.Get(ctx, &appPlc2)).Should(BeNil())
|
||||
appwf1 := model.Workflow{AppPrimaryKey: app1.Name, Name: model.AutoGenWorkflowNamePrefix + app1.Name}
|
||||
|
||||
@@ -36,8 +36,10 @@ import (
|
||||
var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
|
||||
It("Test add function", func() {
|
||||
err := kubeStore.Add(context.TODO(), &model.Application{Name: "kubevela-app", Description: "default"})
|
||||
app := &model.Application{Name: "kubevela-app", Description: "default"}
|
||||
err := kubeStore.Add(context.TODO(), app)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(app.CreateTime.IsZero()).Should(BeFalse())
|
||||
})
|
||||
|
||||
It("Test batch add function", func() {
|
||||
|
||||
@@ -381,6 +381,9 @@ func (p *Parser) parsePoliciesFromRevision(ctx context.Context, af *Appfile) (er
|
||||
return err
|
||||
}
|
||||
for _, policy := range af.Policies {
|
||||
if policy.Properties == nil && policy.Type != v1alpha1.DebugPolicyType {
|
||||
return fmt.Errorf("policy %s named %s must not have empty properties", policy.Type, policy.Name)
|
||||
}
|
||||
switch policy.Type {
|
||||
case v1alpha1.GarbageCollectPolicyType:
|
||||
case v1alpha1.ApplyOncePolicyType:
|
||||
@@ -407,6 +410,9 @@ func (p *Parser) parsePolicies(ctx context.Context, af *Appfile) (err error) {
|
||||
return err
|
||||
}
|
||||
for _, policy := range af.Policies {
|
||||
if policy.Properties == nil && policy.Type != v1alpha1.DebugPolicyType {
|
||||
return fmt.Errorf("policy %s named %s must not have empty properties", policy.Type, policy.Name)
|
||||
}
|
||||
switch policy.Type {
|
||||
case v1alpha1.GarbageCollectPolicyType:
|
||||
case v1alpha1.ApplyOncePolicyType:
|
||||
|
||||
@@ -269,6 +269,20 @@ spec:
|
||||
image: "busybox"
|
||||
`
|
||||
|
||||
const appfileYamlEmptyPolicy = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: application-sample
|
||||
namespace: default
|
||||
spec:
|
||||
components: []
|
||||
policies:
|
||||
- type: garbage-collect
|
||||
name: somename
|
||||
properties:
|
||||
`
|
||||
|
||||
var _ = Describe("Test application parser", func() {
|
||||
It("Test we can parse an application to an appFile", func() {
|
||||
o := v1beta1.Application{}
|
||||
@@ -314,6 +328,14 @@ var _ = Describe("Test application parser", func() {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
_, err = NewApplicationParser(&tclient, dm, pd).GenerateAppFile(context.TODO(), ¬found)
|
||||
Expect(err).Should(HaveOccurred())
|
||||
|
||||
By("app with empty policy")
|
||||
emptyPolicy := v1beta1.Application{}
|
||||
err = yaml.Unmarshal([]byte(appfileYamlEmptyPolicy), &emptyPolicy)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
_, err = NewApplicationParser(&tclient, dm, pd).GenerateAppFile(context.TODO(), &emptyPolicy)
|
||||
Expect(err).Should(HaveOccurred())
|
||||
Expect(err.Error()).Should(ContainSubstring("have empty properties"))
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -86,4 +86,7 @@ type Args struct {
|
||||
|
||||
// IgnoreAppWithoutControllerRequirement indicates that application controller will not process the app without 'app.oam.dev/controller-version-require' annotation.
|
||||
IgnoreAppWithoutControllerRequirement bool
|
||||
|
||||
// IgnoreDefinitionWithoutControllerRequirement indicates that trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation.
|
||||
IgnoreDefinitionWithoutControllerRequirement bool
|
||||
}
|
||||
|
||||
@@ -43,17 +43,24 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// Reconciler reconciles a ComponentDefinition object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
defRevLimit int
|
||||
concurrentReconciles int
|
||||
ignoreDefNoCtrlReq bool
|
||||
controllerVersion string
|
||||
}
|
||||
|
||||
// Reconcile is the main logic for ComponentDefinition controller
|
||||
@@ -68,6 +75,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !r.matchControllerRequirement(&componentDefinition) {
|
||||
klog.InfoS("skip componentDefinition: not match the controller requirement of componentDefinition", "componentDefinition", klog.KObj(&componentDefinition))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// refresh package discover when componentDefinition is registered
|
||||
if componentDefinition.Spec.Workload.Type != types.AutoDetectWorkloadDefinition {
|
||||
err := utils.RefreshPackageDiscover(ctx, r.Client, r.dm, r.pd, &componentDefinition)
|
||||
@@ -187,12 +199,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Setup adds a controller that reconciles ComponentDefinition.
|
||||
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
|
||||
r := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
options: parseOptions(args),
|
||||
}
|
||||
return r.SetupWithManager(mgr)
|
||||
}
|
||||
|
||||
func parseOptions(args oamctrl.Args) options {
|
||||
return options{
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
|
||||
controllerVersion: version.VelaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) matchControllerRequirement(componentDefinition *v1beta1.ComponentDefinition) bool {
|
||||
if componentDefinition.Annotations != nil {
|
||||
if requireVersion, ok := componentDefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
|
||||
return requireVersion == r.controllerVersion
|
||||
}
|
||||
}
|
||||
if r.ignoreDefNoCtrlReq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r = Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
defRevLimit: defRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
options: options{
|
||||
defRevLimit: defRevisionLimit,
|
||||
},
|
||||
}
|
||||
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
|
||||
var ctx context.Context
|
||||
|
||||
@@ -42,17 +42,24 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// Reconciler reconciles a TraitDefinition object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
defRevLimit int
|
||||
concurrentReconciles int
|
||||
ignoreDefNoCtrlReq bool
|
||||
controllerVersion string
|
||||
}
|
||||
|
||||
// Reconcile is the main logic for TraitDefinition controller
|
||||
@@ -67,6 +74,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !r.matchControllerRequirement(&traitdefinition) {
|
||||
klog.InfoS("skip traitDefinition: not match the controller requirement of traitDefinition", "traitDefinition", klog.KObj(&traitdefinition))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// this is a placeholder for finalizer here in the future
|
||||
if traitdefinition.DeletionTimestamp != nil {
|
||||
klog.InfoS("The TraitDefinition is being deleted", "traitDefinition", klog.KRef(req.Namespace, req.Name))
|
||||
@@ -193,12 +205,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Setup adds a controller that reconciles TraitDefinition.
|
||||
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
|
||||
r := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
options: parseOptions(args),
|
||||
}
|
||||
return r.SetupWithManager(mgr)
|
||||
}
|
||||
|
||||
func parseOptions(args oamctrl.Args) options {
|
||||
return options{
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
|
||||
controllerVersion: version.VelaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) matchControllerRequirement(traitDefinition *v1beta1.TraitDefinition) bool {
|
||||
if traitDefinition.Annotations != nil {
|
||||
if requireVersion, ok := traitDefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
|
||||
return requireVersion == r.controllerVersion
|
||||
}
|
||||
}
|
||||
if r.ignoreDefNoCtrlReq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r = Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
defRevLimit: defRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
options: options{
|
||||
defRevLimit: defRevisionLimit,
|
||||
},
|
||||
}
|
||||
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
|
||||
var ctx context.Context
|
||||
|
||||
@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r = Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
defRevLimit: defRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
options: options{
|
||||
defRevLimit: defRevisionLimit,
|
||||
},
|
||||
}
|
||||
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
|
||||
var ctx context.Context
|
||||
|
||||
@@ -42,17 +42,24 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// Reconciler reconciles a WorkflowStepDefinition object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
defRevLimit int
|
||||
concurrentReconciles int
|
||||
ignoreDefNoCtrlReq bool
|
||||
controllerVersion string
|
||||
}
|
||||
|
||||
// Reconcile is the main logic for WorkflowStepDefinition controller
|
||||
@@ -68,6 +75,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !r.matchControllerRequirement(&wfstepdefinition) {
|
||||
klog.InfoS("skip workflowStepDefinition: not match the controller requirement of workflowStepDefinition", "workflowStepDefinition", klog.KObj(&wfstepdefinition))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// this is a placeholder for finalizer here in the future
|
||||
if wfstepdefinition.DeletionTimestamp != nil {
|
||||
return ctrl.Result{}, nil
|
||||
@@ -192,11 +204,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Setup adds a controller that reconciles WorkflowStepDefinition.
|
||||
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
|
||||
r := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
options: parseOptions(args),
|
||||
}
|
||||
return r.SetupWithManager(mgr)
|
||||
}
|
||||
|
||||
func parseOptions(args oamctrl.Args) options {
|
||||
return options{
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
|
||||
controllerVersion: version.VelaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) matchControllerRequirement(wfstepdefinition *v1beta1.WorkflowStepDefinition) bool {
|
||||
if wfstepdefinition.Annotations != nil {
|
||||
if requireVersion, ok := wfstepdefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
|
||||
return requireVersion == r.controllerVersion
|
||||
}
|
||||
}
|
||||
if r.ignoreDefNoCtrlReq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/ast"
|
||||
"cuelang.org/go/cue/format"
|
||||
"cuelang.org/go/cue/parser"
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/pkg/errors"
|
||||
@@ -361,7 +362,7 @@ 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")
|
||||
}
|
||||
output, err := OpenBaiscLit(string(merged))
|
||||
output, err := openJSON(string(merged))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to parse open basic lit for merged result")
|
||||
}
|
||||
@@ -386,9 +387,47 @@ func jsonPatch(base cue.Value, patch cue.Value) (string, error) {
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to apply json patch")
|
||||
}
|
||||
output, err := OpenBaiscLit(string(merged))
|
||||
output, err := openJSON(string(merged))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "failed to parse open basic lit for merged result")
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func isEllipsis(elt ast.Node) bool {
|
||||
_, ok := elt.(*ast.Ellipsis)
|
||||
return ok
|
||||
}
|
||||
|
||||
func openJSON(data string) (string, error) {
|
||||
f, err := parser.ParseFile("-", data, parser.ParseComments)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ast.Walk(f, func(node ast.Node) bool {
|
||||
field, ok := node.(*ast.Field)
|
||||
if ok {
|
||||
v := field.Value
|
||||
switch lit := v.(type) {
|
||||
case *ast.StructLit:
|
||||
if len(lit.Elts) == 0 || !isEllipsis(lit.Elts[len(lit.Elts)-1]) {
|
||||
lit.Elts = append(lit.Elts, &ast.Ellipsis{})
|
||||
}
|
||||
case *ast.ListLit:
|
||||
if len(lit.Elts) == 0 || !isEllipsis(lit.Elts[len(lit.Elts)-1]) {
|
||||
lit.Elts = append(lit.Elts, &ast.Ellipsis{})
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}, nil)
|
||||
if len(f.Decls) > 0 {
|
||||
if emb, ok := f.Decls[0].(*ast.EmbedDecl); ok {
|
||||
if s, _ok := emb.Expr.(*ast.StructLit); _ok {
|
||||
f.Decls = s.Elts
|
||||
}
|
||||
}
|
||||
}
|
||||
b, err := format.Node(f)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
@@ -182,7 +182,8 @@ func peelCloseExpr(node ast.Node) ast.Node {
|
||||
|
||||
func lookField(node ast.Node, key string) ast.Node {
|
||||
if field, ok := node.(*ast.Field); ok {
|
||||
if labelStr(field.Label) == key {
|
||||
// Note: the trim here has side effect: "\(v)" will be trimmed to \(v), only used for comparing fields
|
||||
if strings.Trim(labelStr(field.Label), `"`) == strings.Trim(key, `"`) {
|
||||
return field.Value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,12 @@ const (
|
||||
AliasKey = "definition.oam.dev/alias"
|
||||
// UserPrefix defines the prefix of user customized label or annotation
|
||||
UserPrefix = "custom.definition.oam.dev/"
|
||||
// DefinitionAlias is alias of definition
|
||||
DefinitionAlias = "alias.config.oam.dev"
|
||||
// DefinitionType marks definition's usage type, like image-registry
|
||||
DefinitionType = "type.config.oam.dev"
|
||||
// ConfigCatalog marks definition is a catalog
|
||||
ConfigCatalog = "catalog.config.oam.dev"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -208,7 +208,7 @@ const (
|
||||
// AnnotationWorkloadName indicates the managed workload's name by trait
|
||||
AnnotationWorkloadName = "trait.oam.dev/workload-name"
|
||||
|
||||
// AnnotationControllerRequirement indicates the controller version that can process the application.
|
||||
// AnnotationControllerRequirement indicates the controller version that can process the application/definition.
|
||||
AnnotationControllerRequirement = "app.oam.dev/controller-version-require"
|
||||
|
||||
// AnnotationApplicationServiceAccountName indicates the name of the ServiceAccount to use to apply Components and run Workflow.
|
||||
@@ -227,8 +227,12 @@ const (
|
||||
// AnnotationResourceURL records the source url of the Kubernetes object
|
||||
AnnotationResourceURL = "app.oam.dev/resource-url"
|
||||
|
||||
// AnnotationIgnoreWithoutCompKey indicates the bond component
|
||||
// AnnotationIgnoreWithoutCompKey indicates the bond component.
|
||||
// Deprecated: please use AnnotationAddonDefinitionBindCompKey.
|
||||
AnnotationIgnoreWithoutCompKey = "addon.oam.dev/ignore-without-component"
|
||||
|
||||
// AnnotationAddonDefinitionBondCompKey indicates the definition in addon bond component.
|
||||
AnnotationAddonDefinitionBondCompKey = "addon.oam.dev/bind-component"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -113,7 +113,7 @@ func (cache *resourceCache) exists(manifest *unstructured.Unstructured) bool {
|
||||
return true
|
||||
}
|
||||
appKey, controlledBy := apply.GetAppKey(cache.app), apply.GetControlledBy(manifest)
|
||||
if appKey == controlledBy {
|
||||
if appKey == controlledBy || manifest.GetResourceVersion() == "" {
|
||||
return true
|
||||
}
|
||||
annotations := manifest.GetAnnotations()
|
||||
|
||||
@@ -139,8 +139,9 @@ func TestResourceCacheExistenceCheck(t *testing.T) {
|
||||
createResource := func(appName, appNs, sharedBy string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{oam.LabelAppName: appName, oam.LabelAppNamespace: appNs},
|
||||
"annotations": map[string]interface{}{oam.AnnotationAppSharedBy: sharedBy},
|
||||
"labels": map[string]interface{}{oam.LabelAppName: appName, oam.LabelAppNamespace: appNs},
|
||||
"annotations": map[string]interface{}{oam.AnnotationAppSharedBy: sharedBy},
|
||||
"resourceVersion": "-",
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ func (h *gcHandler) deleteManagedResource(ctx context.Context, mr v1beta1.Manage
|
||||
delete(labels, oam.LabelAppNamespace)
|
||||
entry.obj.SetLabels(labels)
|
||||
}
|
||||
return errors.Wrapf(h.Client.Update(ctx, entry.obj), "failed to remove owner labels for resource while skipping gc")
|
||||
return errors.Wrapf(h.Client.Update(_ctx, entry.obj), "failed to remove owner labels for resource while skipping gc")
|
||||
}
|
||||
if err := h.Client.Delete(_ctx, entry.obj); err != nil && !kerrors.IsNotFound(err) {
|
||||
return errors.Wrapf(err, "failed to delete resource %s", mr.ResourceKey())
|
||||
|
||||
@@ -276,6 +276,7 @@ var _ = Describe("Test ResourceKeeper StateKeep", func() {
|
||||
"metadata": map[string]interface{}{
|
||||
"name": name,
|
||||
"namespace": "default",
|
||||
"labels": map[string]interface{}{oam.LabelAppComponent: name},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replicas": value,
|
||||
|
||||
@@ -8,12 +8,15 @@
|
||||
body: string
|
||||
header: [string]: string
|
||||
trailer: [string]: string
|
||||
...
|
||||
}
|
||||
tls_config?: secret: string
|
||||
response: {
|
||||
body: string
|
||||
header?: [string]: [...string]
|
||||
trailer?: [string]: [...string]
|
||||
statusCode: number
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
@@ -299,7 +299,10 @@ func MustBeControlledByApp(app *v1beta1.Application) ApplyOption {
|
||||
return nil
|
||||
}
|
||||
appKey, controlledBy := GetAppKey(app), GetControlledBy(existing)
|
||||
if controlledBy == "" && !utilfeature.DefaultMutableFeatureGate.Enabled(features.LegacyResourceOwnerValidation) {
|
||||
// if the existing object has no resource version, it means this resource is an API response not directly from
|
||||
// an etcd object but from some external services, such as vela-prism. Then the response does not necessarily
|
||||
// contain the owner
|
||||
if controlledBy == "" && !utilfeature.DefaultMutableFeatureGate.Enabled(features.LegacyResourceOwnerValidation) && existing.GetResourceVersion() != "" {
|
||||
return fmt.Errorf("%s %s/%s exists but not managed by any application now", existing.GetObjectKind().GroupVersionKind().Kind, existing.GetNamespace(), existing.GetName())
|
||||
}
|
||||
if controlledBy != "" && controlledBy != appKey {
|
||||
|
||||
@@ -380,18 +380,20 @@ func TestMustBeControlledByApp(t *testing.T) {
|
||||
hasError: false,
|
||||
},
|
||||
"old app has no label": {
|
||||
existing: &appsv1.Deployment{},
|
||||
existing: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{ResourceVersion: "-"}},
|
||||
hasError: true,
|
||||
},
|
||||
"old app has no app label": {
|
||||
existing: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{},
|
||||
Labels: map[string]string{},
|
||||
ResourceVersion: "-",
|
||||
}},
|
||||
hasError: true,
|
||||
},
|
||||
"old app has no app ns label": {
|
||||
existing: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{oam.LabelAppName: "app"},
|
||||
Labels: map[string]string{oam.LabelAppName: "app"},
|
||||
ResourceVersion: "-",
|
||||
}},
|
||||
hasError: true,
|
||||
},
|
||||
@@ -413,6 +415,18 @@ func TestMustBeControlledByApp(t *testing.T) {
|
||||
}},
|
||||
hasError: true,
|
||||
},
|
||||
"old app has no resource version but with bad app key": {
|
||||
existing: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{oam.LabelAppName: "app", oam.LabelAppNamespace: "ns"},
|
||||
}},
|
||||
hasError: true,
|
||||
},
|
||||
"old app has no resource version": {
|
||||
existing: &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{},
|
||||
}},
|
||||
hasError: false,
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
@@ -96,7 +96,9 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
|
||||
switch req.Operation {
|
||||
case admissionv1.Create:
|
||||
if allErrs := h.ValidateCreate(ctx, app); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, mergeErrors(allErrs))
|
||||
// http.StatusUnprocessableEntity will NOT report any error descriptions
|
||||
// to the client, use generic http.StatusBadRequest instead.
|
||||
return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs))
|
||||
}
|
||||
case admissionv1.Update:
|
||||
oldApp := &v1beta1.Application{}
|
||||
@@ -105,7 +107,7 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
|
||||
}
|
||||
if app.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
if allErrs := h.ValidateUpdate(ctx, app, oldApp); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, mergeErrors(allErrs))
|
||||
return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs))
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -452,4 +452,21 @@ var _ = Describe("Test Application Validator", func() {
|
||||
resp = handler.Handle(ctx, req)
|
||||
Expect(resp.Allowed).Should(BeFalse())
|
||||
})
|
||||
|
||||
It("Test Application with empty policy", func() {
|
||||
req := admission.Request{
|
||||
AdmissionRequest: admissionv1.AdmissionRequest{
|
||||
Operation: admissionv1.Create,
|
||||
Resource: metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1beta1", Resource: "applications"},
|
||||
Object: runtime.RawExtension{
|
||||
Raw: []byte(`
|
||||
{"kind":"Application","metadata":{"name":"app-with-empty-policy-webhook-test", "namespace":"default"},
|
||||
"spec":{"components":[],"policies":[{"name":"2345","type":"garbage-collect","properties":null}]}}
|
||||
`),
|
||||
},
|
||||
},
|
||||
}
|
||||
resp := handler.Handle(ctx, req)
|
||||
Expect(resp.Allowed).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -118,13 +118,14 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
|
||||
if err := h.Decoder.DecodeRaw(req.AdmissionRequest.OldObject, oldApp); err != nil {
|
||||
return admission.Errored(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
if allErrs := h.ValidateUpdate(ctx, app, oldApp); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, allErrs.ToAggregate())
|
||||
// http.StatusUnprocessableEntity will NOT report any error descriptions
|
||||
// to the client, use generic http.StatusBadRequest instead.
|
||||
return admission.Errored(http.StatusBadRequest, allErrs.ToAggregate())
|
||||
}
|
||||
case admissionv1.Create:
|
||||
if allErrs := h.ValidateCreate(ctx, app); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, allErrs.ToAggregate())
|
||||
return admission.Errored(http.StatusBadRequest, allErrs.ToAggregate())
|
||||
}
|
||||
default:
|
||||
// Do nothing for CONNECT
|
||||
|
||||
@@ -224,7 +224,7 @@ func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient cli
|
||||
fmt.Println()
|
||||
fmt.Println(` vela port-forward -n vela-system addon-velaux 9082:80`)
|
||||
fmt.Println()
|
||||
fmt.Println(`Select "Cluster: local | Namespace: vela-system | Kind: Service | Name: velaux" from the prompt.`)
|
||||
fmt.Println(`Select "local | velaux | velaux" from the prompt.`)
|
||||
fmt.Println()
|
||||
fmt.Println(`Please refer to https://kubevela.io/docs/reference/addons/velaux for more VelaUX addon installation and visiting method.`)
|
||||
}
|
||||
|
||||
@@ -118,13 +118,20 @@ func (d *debugOpts) debugApplication(ctx context.Context, c common.Args, app *v1
|
||||
}
|
||||
|
||||
// debug workflow steps
|
||||
rawValue, err := d.getDebugRawValue(ctx, cli, pd, app)
|
||||
rawValue, data, err := d.getDebugRawValue(ctx, cli, pd, app)
|
||||
if err != nil {
|
||||
if data != "" {
|
||||
ioStreams.Info(color.RedString("%s%s", emojiFail, err.Error()))
|
||||
ioStreams.Info(color.GreenString("Original Data in Debug:\n"), data)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.handleCueSteps(rawValue, ioStreams); err != nil {
|
||||
return err
|
||||
ioStreams.Info(color.RedString("%s%s", emojiFail, err.Error()))
|
||||
ioStreams.Info(color.GreenString("Original Data in Debug:\n"), data)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// dry run components
|
||||
@@ -252,20 +259,20 @@ func unwrapStepName(step string) string {
|
||||
return step
|
||||
}
|
||||
|
||||
func (d *debugOpts) getDebugRawValue(ctx context.Context, cli client.Client, pd *packages.PackageDiscover, app *v1beta1.Application) (*value.Value, error) {
|
||||
func (d *debugOpts) getDebugRawValue(ctx context.Context, cli client.Client, pd *packages.PackageDiscover, app *v1beta1.Application) (*value.Value, string, error) {
|
||||
debugCM := &corev1.ConfigMap{}
|
||||
if err := cli.Get(ctx, client.ObjectKey{Name: debug.GenerateContextName(app.Name, d.step), Namespace: app.Namespace}, debugCM); err != nil {
|
||||
return nil, fmt.Errorf("failed to get debug configmap, please make sure your application have the debug policy, you can add the debug policy by using `vela up -f <app.yaml> --debug`: %w", err)
|
||||
return nil, "", fmt.Errorf("failed to get debug configmap, please make sure your application have the debug policy, you can add the debug policy by using `vela up -f <app.yaml> --debug`: %w", err)
|
||||
}
|
||||
|
||||
if debugCM.Data == nil || debugCM.Data["debug"] == "" {
|
||||
return nil, fmt.Errorf("debug configmap is empty")
|
||||
return nil, "", fmt.Errorf("debug configmap is empty")
|
||||
}
|
||||
v, err := value.NewValue(debugCM.Data["debug"], pd, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse debug configmap: %w", err)
|
||||
return nil, debugCM.Data["debug"], fmt.Errorf("failed to parse debug configmap: %w", err)
|
||||
}
|
||||
return v, nil
|
||||
return v, debugCM.Data["debug"], nil
|
||||
}
|
||||
|
||||
func (d *debugOpts) handleCueSteps(v *value.Value, ioStreams cmdutil.IOStreams) error {
|
||||
|
||||
@@ -93,9 +93,7 @@ func TestDebugApplicationWithWorkflow(t *testing.T) {
|
||||
"debug": "error",
|
||||
},
|
||||
},
|
||||
step: "test-wf1",
|
||||
focus: "test",
|
||||
expectedErr: "failed to parse debug configmap",
|
||||
step: "test-wf1",
|
||||
},
|
||||
"success": {
|
||||
app: &v1beta1.Application{
|
||||
|
||||
@@ -289,7 +289,7 @@ func NewDefinitionInitCommand(c common.Args) *cobra.Command {
|
||||
cmd.Flags().StringP(FlagTemplateYAML, "f", "", "Specify the template yaml file that definition will use to build the schema. If empty, a default template for the given definition type will be used.")
|
||||
cmd.Flags().StringP(FlagOutput, "o", "", "Specify the output path of the generated definition. If empty, the definition will be printed in the console.")
|
||||
cmd.Flags().BoolP(FlagInteractive, "i", false, "Specify whether use interactive process to help generate definitions.")
|
||||
cmd.Flags().StringP(FlagProvider, "p", "", "Specify which provider the cloud resource definition belongs to. Only `alibaba`, `aws`, `azure` are supported.")
|
||||
cmd.Flags().StringP(FlagProvider, "p", "", "Specify which provider the cloud resource definition belongs to. Only `alibaba`, `aws`, `azure`, `gcp`, `baidu`, `tencent`, `elastic`, `ucloud`, `vsphere` are supported.")
|
||||
cmd.Flags().StringP(FlagGit, "", "", "Specify which git repository the configuration(HCL) is stored in. Valid when --provider/-p is set.")
|
||||
cmd.Flags().StringP(FlagLocal, "", "", "Specify the local path of the configuration(HCL) file. Valid when --provider/-p is set.")
|
||||
cmd.Flags().StringP(FlagPath, "", "", "Specify which path the configuration(HCL) is stored in the Git repository. Valid when --git is set.")
|
||||
@@ -302,7 +302,7 @@ func generateTerraformTypedComponentDefinition(cmd *cobra.Command, name, kind, p
|
||||
}
|
||||
|
||||
switch provider {
|
||||
case "aws", "azure", "alibaba", "tencent", "gcp", "baidu", "elastic", "ucloud":
|
||||
case "aws", "azure", "alibaba", "tencent", "gcp", "baidu", "elastic", "ucloud", "vsphere":
|
||||
var terraform *commontype.Terraform
|
||||
|
||||
git, err := cmd.Flags().GetString(FlagGit)
|
||||
@@ -378,7 +378,7 @@ func generateTerraformTypedComponentDefinition(cmd *cobra.Command, name, kind, p
|
||||
}
|
||||
return out.String(), nil
|
||||
default:
|
||||
return "", errors.Errorf("Provider `%s` is not supported. Only `alibaba`, `aws`, `azure`, `gcp`, `baidu`, `tencent`, `elastic`, `ucloud` are supported.", provider)
|
||||
return "", errors.Errorf("Provider `%s` is not supported. Only `alibaba`, `aws`, `azure`, `gcp`, `baidu`, `tencent`, `elastic`, `ucloud`, `vsphere` are supported.", provider)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ var (
|
||||
Apply Kubernetes objects in clusters
|
||||
|
||||
Apply Kubernetes objects in multiple clusters. Use --clusters to specify which clusters to
|
||||
apply. If -n/--namespace is used, the original object namespace will be overrode.
|
||||
apply. If -n/--namespace is used, the original object namespace will be overridden.
|
||||
|
||||
You can use -f/--file to specify the object file/folder to apply. Multiple file inputs are allowed.
|
||||
Directory input and web url input is supported as well.
|
||||
|
||||
@@ -297,12 +297,25 @@ func listProviders(ctx context.Context, k8sClient client.Client, ioStreams cmdut
|
||||
// getTerraformProviderTypes retrieves all ComponentDefinition for Terraform Cloud Providers which are delivered by
|
||||
// Terraform Cloud provider addons
|
||||
func getTerraformProviderTypes(ctx context.Context, k8sClient client.Client) ([]v1beta1.ComponentDefinition, error) {
|
||||
defs := &v1beta1.ComponentDefinitionList{}
|
||||
if err := k8sClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{definition.UserPrefix + "type.config.oam.dev": types.TerraformProvider}); err != nil {
|
||||
// keep compatibility with old version of terraform-xxx provider definition
|
||||
legacyDefs := &v1beta1.ComponentDefinitionList{}
|
||||
if err := k8sClient.List(ctx, legacyDefs, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{definition.UserPrefix + definition.DefinitionType: types.TerraformProvider}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return defs.Items, nil
|
||||
defs := &v1beta1.ComponentDefinitionList{}
|
||||
if err := k8sClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS),
|
||||
client.MatchingLabels{definition.DefinitionType: types.TerraformProvider}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []v1beta1.ComponentDefinition
|
||||
result = append(result, legacyDefs.Items...)
|
||||
result = append(result, defs.Items...)
|
||||
if len(result) == 0 {
|
||||
return nil, errors.New("no Terraform Cloud Provider ComponentDefinition found")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// getTerraformProviderType retrieves the ComponentDefinition for a Terraform Cloud Provider which is delivered by
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"testing"
|
||||
|
||||
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -31,6 +32,7 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/definition"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
@@ -93,3 +95,63 @@ func TestListProviders(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProviderTypes(t *testing.T) {
|
||||
s := runtime.NewScheme()
|
||||
v1beta1.AddToScheme(s)
|
||||
corev1.AddToScheme(s)
|
||||
terraformapi.AddToScheme(s)
|
||||
|
||||
p1 := &v1beta1.ComponentDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "p1",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{definition.DefinitionType: types.TerraformProvider},
|
||||
},
|
||||
}
|
||||
p2 := &v1beta1.ComponentDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "p2",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Labels: map[string]string{definition.UserPrefix + definition.DefinitionType: types.TerraformProvider},
|
||||
},
|
||||
}
|
||||
testCases := map[string]struct {
|
||||
objects []client.Object
|
||||
gotTypes map[string]bool
|
||||
wantErr error
|
||||
}{
|
||||
"success": {
|
||||
objects: []client.Object{p1},
|
||||
gotTypes: map[string]bool{
|
||||
"p1": true,
|
||||
},
|
||||
},
|
||||
"success with legacy provider": {
|
||||
objects: []client.Object{p2},
|
||||
gotTypes: map[string]bool{
|
||||
"p2": true,
|
||||
},
|
||||
},
|
||||
"not found": {
|
||||
objects: []client.Object{},
|
||||
wantErr: errors.New("no Terraform Cloud Provider ComponentDefinition found"),
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
for _, tc := range testCases {
|
||||
k8sClient = fake.NewClientBuilder().WithScheme(s).WithObjects(tc.objects...).Build()
|
||||
providerTypes, err := getTerraformProviderTypes(ctx, k8sClient)
|
||||
if tc.wantErr != nil {
|
||||
assert.Contains(t, err.Error(), tc.wantErr.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(tc.gotTypes), len(providerTypes))
|
||||
for _, gotType := range providerTypes {
|
||||
_, found := tc.gotTypes[gotType.Name]
|
||||
assert.True(t, found)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ var (
|
||||
vela up example-app -n example-ns --publish-version beta --revision example-app-v2
|
||||
|
||||
# Deploy an application from stdin
|
||||
cat <<EOF | vela up vela up -f -
|
||||
cat <<EOF | vela up -f -
|
||||
... <app.yaml here> ...
|
||||
EOF
|
||||
`))
|
||||
|
||||
72
references/docgen/def-doc/policy/shared-resource.eg.md
Normal file
72
references/docgen/def-doc/policy/shared-resource.eg.md
Normal file
@@ -0,0 +1,72 @@
|
||||
It's used in [shared-resource](https://kubevela.io/docs/end-user/policies/shared-resource) scenario.
|
||||
It can be used to configure which resources can be shared between applications. The target resource will allow multiple application to read it but only the first one to be able to write it.
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: app1
|
||||
spec:
|
||||
components:
|
||||
- name: ns1
|
||||
type: k8s-objects
|
||||
properties:
|
||||
objects:
|
||||
- apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: example
|
||||
- name: cm1
|
||||
type: k8s-objects
|
||||
properties:
|
||||
objects:
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm1
|
||||
namespace: example
|
||||
data:
|
||||
key: value1
|
||||
policies:
|
||||
- name: shared-resource
|
||||
type: shared-resource
|
||||
properties:
|
||||
rules:
|
||||
- selector:
|
||||
resourceTypes: ["Namespace"]
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: app2
|
||||
spec:
|
||||
components:
|
||||
- name: ns2
|
||||
type: k8s-objects
|
||||
properties:
|
||||
objects:
|
||||
- apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: example
|
||||
- name: cm2
|
||||
type: k8s-objects
|
||||
properties:
|
||||
objects:
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm2
|
||||
namespace: example
|
||||
data:
|
||||
key: value2
|
||||
policies:
|
||||
- name: shared-resource
|
||||
type: shared-resource
|
||||
properties:
|
||||
rules:
|
||||
- selector:
|
||||
resourceTypes: ["Namespace"]
|
||||
```
|
||||
@@ -146,20 +146,23 @@ var _ = Describe("Test addon rest api", func() {
|
||||
"testkey": "new-testvalue",
|
||||
},
|
||||
}
|
||||
res := put("/addons/mock-addon/update", req)
|
||||
defer res.Body.Close()
|
||||
var addonStatus apisv1.AddonStatusResponse
|
||||
Expect(decodeResponseBody(res, &addonStatus)).Should(Succeed())
|
||||
Expect(addonStatus.Name).Should(BeEquivalentTo("mock-addon"))
|
||||
Expect(len(addonStatus.Args)).Should(BeEquivalentTo(1))
|
||||
Expect(addonStatus.Args["testkey"]).Should(BeEquivalentTo("new-testvalue"))
|
||||
Eventually(func(g Gomega) {
|
||||
res := put("/addons/mock-addon/update", req)
|
||||
defer res.Body.Close()
|
||||
var addonStatus apisv1.AddonStatusResponse
|
||||
g.Expect(decodeResponseBody(res, &addonStatus)).Should(Succeed())
|
||||
g.Expect(addonStatus.Name).Should(BeEquivalentTo("mock-addon"))
|
||||
g.Expect(len(addonStatus.Args)).Should(BeEquivalentTo(1))
|
||||
g.Expect(addonStatus.Args["testkey"]).Should(BeEquivalentTo("new-testvalue"))
|
||||
|
||||
status := get("/addons/mock-addon/status")
|
||||
var newaddonStatus apisv1.AddonStatusResponse
|
||||
g.Expect(decodeResponseBody(status, &newaddonStatus)).Should(Succeed())
|
||||
g.Expect(newaddonStatus.Name).Should(BeEquivalentTo("mock-addon"))
|
||||
g.Expect(len(newaddonStatus.Args)).Should(BeEquivalentTo(1))
|
||||
g.Expect(newaddonStatus.Args["testkey"]).Should(BeEquivalentTo("new-testvalue"))
|
||||
}, 15*time.Second).Should(Succeed())
|
||||
|
||||
status := get("/addons/mock-addon/status")
|
||||
var newaddonStatus apisv1.AddonStatusResponse
|
||||
Expect(decodeResponseBody(status, &newaddonStatus)).Should(Succeed())
|
||||
Expect(newaddonStatus.Name).Should(BeEquivalentTo("mock-addon"))
|
||||
Expect(len(newaddonStatus.Args)).Should(BeEquivalentTo(1))
|
||||
Expect(newaddonStatus.Args["testkey"]).Should(BeEquivalentTo("new-testvalue"))
|
||||
})
|
||||
|
||||
It("list enabled addon", func() {
|
||||
|
||||
@@ -43,7 +43,11 @@ import (
|
||||
)
|
||||
|
||||
var _ = Describe("Test multicluster standalone scenario", func() {
|
||||
|
||||
waitObject := func(ctx context.Context, un unstructured.Unstructured) {
|
||||
Eventually(func(g Gomega) error {
|
||||
return k8sClient.Get(ctx, client.ObjectKeyFromObject(&un), &un)
|
||||
}, 10*time.Second).Should(Succeed())
|
||||
}
|
||||
var namespace string
|
||||
var hubCtx context.Context
|
||||
var workerCtx context.Context
|
||||
@@ -116,10 +120,13 @@ var _ = Describe("Test multicluster standalone scenario", func() {
|
||||
|
||||
deploy := readFile("deployment.yaml")
|
||||
Expect(k8sClient.Create(hubCtx, deploy)).Should(Succeed())
|
||||
waitObject(hubCtx, *deploy)
|
||||
workflow := readFile("workflow-suspend.yaml")
|
||||
Expect(k8sClient.Create(hubCtx, workflow)).Should(Succeed())
|
||||
waitObject(hubCtx, *workflow)
|
||||
policy := readFile("policy-zero-replica.yaml")
|
||||
Expect(k8sClient.Create(hubCtx, policy)).Should(Succeed())
|
||||
waitObject(hubCtx, *policy)
|
||||
app := readFile("app-with-publish-version.yaml")
|
||||
Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user