mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-02 01:30:47 +00:00
Compare commits
30 Commits
v1.2.0-bet
...
v1.2.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f468814371 | ||
|
|
a1b1d4a6f8 | ||
|
|
82453b45f5 | ||
|
|
72a00b57e6 | ||
|
|
ff4b10f0ee | ||
|
|
746eb0dbe4 | ||
|
|
a33d1e488a | ||
|
|
0d6173c1ca | ||
|
|
00a80b9ecb | ||
|
|
8284581e0c | ||
|
|
8a5759949a | ||
|
|
820db96eae | ||
|
|
4d69027300 | ||
|
|
5210800cac | ||
|
|
66881c13d3 | ||
|
|
5648c56cf5 | ||
|
|
27252f32de | ||
|
|
775faee96f | ||
|
|
e818921a87 | ||
|
|
551992e8f2 | ||
|
|
e519c6142a | ||
|
|
3198693ad7 | ||
|
|
88aa6c0e83 | ||
|
|
8b82a79d1d | ||
|
|
14a57fc656 | ||
|
|
d7ee46134d | ||
|
|
6e5e26c19d | ||
|
|
8750fc8fab | ||
|
|
7ed293e27e | ||
|
|
36ad77493c |
4
.github/workflows/e2e-rollout-test.yml
vendored
4
.github/workflows/e2e-rollout-test.yml
vendored
@@ -90,12 +90,12 @@ jobs:
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: /tmp/e2e-profile.out,/tmp/oam-e2e-profile.out
|
||||
files: /tmp/e2e-profile.out
|
||||
flags: e2e-rollout-tests
|
||||
name: codecov-umbrella
|
||||
|
||||
- name: Clean e2e profile
|
||||
run: rm /tmp/e2e-profile.out /tmp/oam-e2e-profile.out
|
||||
run: rm /tmp/e2e-profile.out
|
||||
|
||||
- name: Cleanup image
|
||||
if: ${{ always() }}
|
||||
|
||||
4
.github/workflows/e2e-test.yml
vendored
4
.github/workflows/e2e-test.yml
vendored
@@ -96,12 +96,12 @@ jobs:
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: /tmp/e2e-profile.out,/tmp/oam-e2e-profile.out
|
||||
files: /tmp/e2e-profile.out
|
||||
flags: e2etests
|
||||
name: codecov-umbrella
|
||||
|
||||
- name: Clean e2e profile
|
||||
run: rm /tmp/e2e-profile.out /tmp/oam-e2e-profile.out
|
||||
run: rm /tmp/e2e-profile.out
|
||||
|
||||
- name: Cleanup image
|
||||
if: ${{ always() }}
|
||||
|
||||
46
.github/workflows/go.yml
vendored
46
.github/workflows/go.yml
vendored
@@ -33,52 +33,6 @@ jobs:
|
||||
do_not_skip: '["workflow_dispatch", "schedule", "push"]'
|
||||
concurrent_skipping: false
|
||||
|
||||
compatibility-test:
|
||||
runs-on: ubuntu-20.04
|
||||
needs: detect-noop
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Cache Go Dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: .work/pkg
|
||||
key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-pkg-
|
||||
|
||||
- name: Install ginkgo
|
||||
run: |
|
||||
sudo apt-get install -y golang-ginkgo-dev
|
||||
|
||||
- name: Setup Kind Cluster
|
||||
uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
version: ${{ env.KIND_VERSION }}
|
||||
|
||||
- name: install Kubebuilder
|
||||
uses: RyanSiu1995/kubebuilder-action@v1.2
|
||||
with:
|
||||
version: 3.1.0
|
||||
kubebuilderOnly: false
|
||||
kubernetesVersion: v1.21.2
|
||||
|
||||
- name: Run Make compatibility-test
|
||||
run: make compatibility-test
|
||||
|
||||
- name: Clean up testdata
|
||||
run: make compatibility-testdata-cleanup
|
||||
|
||||
staticcheck:
|
||||
runs-on: ubuntu-20.04
|
||||
needs: detect-noop
|
||||
|
||||
9
.github/workflows/registry.yml
vendored
9
.github/workflows/registry.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
- name: Build & Pushing vela-core for ACR
|
||||
run: |
|
||||
docker build -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }} .
|
||||
docker build --build-arg GOPROXY=https://proxy.golang.org -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }} .
|
||||
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing vela-core for Dockerhub and GHCR
|
||||
@@ -72,13 +72,14 @@ jobs:
|
||||
build-args: |
|
||||
GITVERSION=git-${{ steps.vars.outputs.git_revision }}
|
||||
VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||
GOPROXY=https://proxy.golang.org
|
||||
tags: |-
|
||||
docker.io/oamdev/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository }}/vela-core:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
- name: Build & Pushing vela-apiserver for ACR
|
||||
run: |
|
||||
docker build -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }} -f Dockerfile.apiserver .
|
||||
docker build --build-arg GOPROXY=https://proxy.golang.org -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }} -f Dockerfile.apiserver .
|
||||
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing vela-apiserver for Dockerhub and GHCR
|
||||
@@ -93,13 +94,14 @@ jobs:
|
||||
build-args: |
|
||||
GITVERSION=git-${{ steps.vars.outputs.git_revision }}
|
||||
VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||
GOPROXY=https://proxy.golang.org
|
||||
tags: |-
|
||||
docker.io/oamdev/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository }}/vela-apiserver:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
- name: Build & Pushing vela runtime rollout for ACR
|
||||
run: |
|
||||
docker build -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }} .
|
||||
docker build --build-arg GOPROXY=https://proxy.golang.org -t kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }} .
|
||||
docker push kubevela-registry.cn-hangzhou.cr.aliyuncs.com/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
|
||||
- uses: docker/build-push-action@v2
|
||||
name: Build & Pushing runtime rollout for Dockerhub and GHCR
|
||||
@@ -114,6 +116,7 @@ jobs:
|
||||
build-args: |
|
||||
GITVERSION=git-${{ steps.vars.outputs.git_revision }}
|
||||
VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||
GOPROXY=https://proxy.golang.org
|
||||
tags: |-
|
||||
docker.io/oamdev/vela-rollout:${{ steps.get_version.outputs.VERSION }}
|
||||
ghcr.io/${{ github.repository }}/vela-rollout:${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
@@ -8,7 +8,8 @@ COPY go.mod go.mod
|
||||
COPY go.sum go.sum
|
||||
|
||||
# It's a proxy for CN developer, please unblock it if you have network issue
|
||||
# RUN go env -w GOPROXY=https://goproxy.cn,direct
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-https://goproxy.cn}
|
||||
|
||||
# cache deps before building and copying source so that we don't need to re-download as much
|
||||
# and so that source changes don't invalidate our downloaded layer
|
||||
|
||||
268
Makefile
268
Makefile
@@ -1,51 +1,11 @@
|
||||
SHELL := /bin/bash
|
||||
|
||||
# Vela version
|
||||
VELA_VERSION ?= master
|
||||
# Repo info
|
||||
GIT_COMMIT ?= git-$(shell git rev-parse --short HEAD)
|
||||
GIT_COMMIT_LONG ?= $(shell git rev-parse HEAD)
|
||||
VELA_VERSION_KEY := github.com/oam-dev/kubevela/version.VelaVersion
|
||||
VELA_GITVERSION_KEY := github.com/oam-dev/kubevela/version.GitRevision
|
||||
LDFLAGS ?= "-s -w -X $(VELA_VERSION_KEY)=$(VELA_VERSION) -X $(VELA_GITVERSION_KEY)=$(GIT_COMMIT)"
|
||||
|
||||
GOBUILD_ENV = GO111MODULE=on CGO_ENABLED=0
|
||||
GOX = go run github.com/mitchellh/gox
|
||||
TARGETS := darwin/amd64 linux/amd64 windows/amd64
|
||||
DIST_DIRS := find * -type d -exec
|
||||
|
||||
TIME_LONG = `date +%Y-%m-%d' '%H:%M:%S`
|
||||
TIME_SHORT = `date +%H:%M:%S`
|
||||
TIME = $(TIME_SHORT)
|
||||
|
||||
BLUE := $(shell printf "\033[34m")
|
||||
YELLOW := $(shell printf "\033[33m")
|
||||
RED := $(shell printf "\033[31m")
|
||||
GREEN := $(shell printf "\033[32m")
|
||||
CNone := $(shell printf "\033[0m")
|
||||
|
||||
INFO = echo ${TIME} ${BLUE}[ .. ]${CNone}
|
||||
WARN = echo ${TIME} ${YELLOW}[WARN]${CNone}
|
||||
ERR = echo ${TIME} ${RED}[FAIL]${CNone}
|
||||
OK = echo ${TIME} ${GREEN}[ OK ]${CNone}
|
||||
FAIL = (echo ${TIME} ${RED}[FAIL]${CNone} && false)
|
||||
|
||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||
ifeq (,$(shell go env GOBIN))
|
||||
GOBIN=$(shell go env GOPATH)/bin
|
||||
else
|
||||
GOBIN=$(shell go env GOBIN)
|
||||
endif
|
||||
|
||||
# Image URL to use all building/pushing image targets
|
||||
VELA_CORE_IMAGE ?= vela-core:latest
|
||||
VELA_CORE_TEST_IMAGE ?= vela-core-test:$(GIT_COMMIT)
|
||||
VELA_APISERVER_IMAGE ?= apiserver:latest
|
||||
VELA_RUNTIME_ROLLOUT_IMAGE ?= vela-runtime-rollout:latest
|
||||
VELA_RUNTIME_ROLLOUT_TEST_IMAGE ?= vela-runtime-rollout-test:$(GIT_COMMIT)
|
||||
RUNTIME_CLUSTER_CONFIG ?= /tmp/worker.kubeconfig
|
||||
RUNTIME_CLUSTER_NAME ?= worker
|
||||
include makefiles/const.mk
|
||||
include makefiles/dependency.mk
|
||||
include makefiles/release.mk
|
||||
include makefiles/develop.mk
|
||||
include makefiles/build.mk
|
||||
include makefiles/e2e.mk
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
all: build
|
||||
|
||||
# Run tests
|
||||
@@ -62,51 +22,9 @@ unit-test-apiserver:
|
||||
build: fmt vet lint staticcheck vela-cli kubectl-vela
|
||||
@$(OK) build succeed
|
||||
|
||||
vela-cli:
|
||||
$(GOBUILD_ENV) go build -o bin/vela -a -ldflags $(LDFLAGS) ./references/cmd/cli/main.go
|
||||
|
||||
kubectl-vela:
|
||||
$(GOBUILD_ENV) go build -o bin/kubectl-vela -a -ldflags $(LDFLAGS) ./cmd/plugin/main.go
|
||||
|
||||
doc-gen:
|
||||
rm -r docs/en/cli/*
|
||||
go run hack/docgen/gen.go
|
||||
|
||||
cross-build:
|
||||
rm -rf _bin
|
||||
go get github.com/mitchellh/gox@v0.4.0
|
||||
$(GOBUILD_ENV) $(GOX) -ldflags $(LDFLAGS) -parallel=2 -output="_bin/vela/{{.OS}}-{{.Arch}}/vela" -osarch='$(TARGETS)' ./references/cmd/cli
|
||||
$(GOBUILD_ENV) $(GOX) -ldflags $(LDFLAGS) -parallel=2 -output="_bin/kubectl-vela/{{.OS}}-{{.Arch}}/kubectl-vela" -osarch='$(TARGETS)' ./cmd/plugin
|
||||
$(GOBUILD_ENV) $(GOX) -ldflags $(LDFLAGS) -parallel=2 -output="_bin/apiserver/{{.OS}}-{{.Arch}}/apiserver" -osarch="$(TARGETS)" ./cmd/apiserver
|
||||
|
||||
build-cleanup:
|
||||
rm -rf _bin
|
||||
|
||||
compress:
|
||||
( \
|
||||
echo "\n## Release Info\nVERSION: $(VELA_VERSION)" >> README.md && \
|
||||
echo "GIT_COMMIT: $(GIT_COMMIT_LONG)\n" >> README.md && \
|
||||
cd _bin/vela && \
|
||||
$(DIST_DIRS) cp ../../LICENSE {} \; && \
|
||||
$(DIST_DIRS) cp ../../README.md {} \; && \
|
||||
$(DIST_DIRS) tar -zcf vela-{}.tar.gz {} \; && \
|
||||
$(DIST_DIRS) zip -r vela-{}.zip {} \; && \
|
||||
cd ../kubectl-vela && \
|
||||
$(DIST_DIRS) cp ../../LICENSE {} \; && \
|
||||
$(DIST_DIRS) cp ../../README.md {} \; && \
|
||||
$(DIST_DIRS) tar -zcf kubectl-vela-{}.tar.gz {} \; && \
|
||||
$(DIST_DIRS) zip -r kubectl-vela-{}.zip {} \; && \
|
||||
cd .. && \
|
||||
sha256sum vela/vela-* kubectl-vela/kubectl-vela-* > sha256sums.txt \
|
||||
)
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config
|
||||
run:
|
||||
go run ./cmd/core/main.go --application-revision-limit 5
|
||||
|
||||
run-apiserver:
|
||||
go run ./cmd/apiserver/main.go
|
||||
|
||||
# Run go fmt against code
|
||||
fmt: goimports installcue
|
||||
go fmt ./...
|
||||
@@ -136,93 +54,14 @@ check-diff: reviewable
|
||||
git diff --quiet || ($(ERR) please run 'make reviewable' to include all changes && false)
|
||||
@$(OK) branch is clean
|
||||
|
||||
# Build the docker image
|
||||
docker-build: docker-build-core docker-build-apiserver
|
||||
@$(OK)
|
||||
docker-build-core:
|
||||
docker build --build-arg=VERSION=$(VELA_VERSION) --build-arg=GITVERSION=$(GIT_COMMIT) -t $(VELA_CORE_IMAGE) .
|
||||
docker-build-apiserver:
|
||||
docker build --build-arg=VERSION=$(VELA_VERSION) --build-arg=GITVERSION=$(GIT_COMMIT) -t $(VELA_APISERVER_IMAGE) -f Dockerfile.apiserver .
|
||||
|
||||
# Build the runtime docker image
|
||||
docker-build-runtime-rollout:
|
||||
docker build --build-arg=VERSION=$(VELA_VERSION) --build-arg=GITVERSION=$(GIT_COMMIT) -t $(VELA_RUNTIME_ROLLOUT_IMAGE) -f runtime/rollout/Dockerfile .
|
||||
|
||||
# Push the docker image
|
||||
docker-push:
|
||||
docker push $(VELA_CORE_IMAGE)
|
||||
|
||||
e2e-setup-core:
|
||||
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
|
||||
kubectl wait --for=condition=Available deployment/kubevela-vela-core -n vela-system --timeout=180s
|
||||
go run ./e2e/addon/mock &
|
||||
|
||||
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
|
||||
|
||||
e2e-setup:
|
||||
helm install kruise https://github.com/openkruise/kruise/releases/download/v0.9.0/kruise-chart.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 --create-namespace --namespace oam-runtime-system --set image.pullPolicy=IfNotPresent --set image.repository=vela-core-test --set dependCheckWait=10s --set image.tag=$(GIT_COMMIT) --wait oam-runtime ./charts/oam-runtime
|
||||
go run ./e2e/addon/mock &
|
||||
bin/vela addon enable fluxcd
|
||||
bin/vela addon enable terraform
|
||||
bin/vela addon enable terraform-alibaba ALICLOUD_ACCESS_KEY=xxx ALICLOUD_SECRET_KEY=yyy ALICLOUD_REGION=cn-beijing
|
||||
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
|
||||
|
||||
build-swagger:
|
||||
go run ./cmd/apiserver/main.go build-swagger ./docs/apidoc/swagger.json
|
||||
e2e-api-test:
|
||||
# Run e2e test
|
||||
ginkgo -v -skipPackage capability,setup,application -r e2e
|
||||
ginkgo -v -r e2e/application
|
||||
|
||||
e2e-apiserver-test: build-swagger
|
||||
go run ./e2e/addon/mock &
|
||||
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_apiserver_test.out ./test/e2e-apiserver-test
|
||||
@$(OK) tests pass
|
||||
|
||||
e2e-test:
|
||||
# Run e2e test
|
||||
ginkgo -v --skip="rollout related e2e-test." ./test/e2e-test
|
||||
@$(OK) tests pass
|
||||
|
||||
e2e-addon-test:
|
||||
cp bin/vela /tmp/
|
||||
ginkgo -v ./test/e2e-addon-test
|
||||
@$(OK) tests pass
|
||||
|
||||
e2e-rollout-test:
|
||||
ginkgo -v --focus="rollout related e2e-test." ./test/e2e-test
|
||||
@$(OK) tests pass
|
||||
|
||||
e2e-multicluster-test:
|
||||
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_multicluster_test.out ./test/e2e-multicluster-test
|
||||
@$(OK) tests pass
|
||||
|
||||
compatibility-test: vet lint staticcheck generate-compatibility-testdata
|
||||
# Run compatibility test with old crd
|
||||
COMPATIBILITY_TEST=TRUE go test -race $(shell go list ./pkg/... | grep -v apiserver)
|
||||
@$(OK) compatibility-test pass
|
||||
|
||||
generate-compatibility-testdata:
|
||||
mkdir -p ./test/compatibility-test/testdata
|
||||
go run ./test/compatibility-test/convert/main.go ./charts/vela-core/crds ./test/compatibility-test/testdata
|
||||
|
||||
compatibility-testdata-cleanup:
|
||||
rm -f ./test/compatibility-test/testdata/*
|
||||
|
||||
e2e-cleanup:
|
||||
# Clean up
|
||||
rm -rf ~/.vela
|
||||
|
||||
image-cleanup:
|
||||
ifneq (, $(shell which docker))
|
||||
@@ -238,11 +77,7 @@ endif
|
||||
|
||||
endif
|
||||
|
||||
end-e2e-core:
|
||||
sh ./hack/e2e/end_e2e_core.sh
|
||||
|
||||
end-e2e:
|
||||
sh ./hack/e2e/end_e2e.sh
|
||||
|
||||
# load docker image to the kind cluster
|
||||
kind-load:
|
||||
@@ -267,24 +102,6 @@ manager: fmt vet lint manifests
|
||||
vela-runtime-rollout-manager: fmt vet lint manifests
|
||||
$(GOBUILD_ENV) go build -o ./runtime/rollout/bin/manager -a -ldflags $(LDFLAGS) ./runtime/rollout/cmd/main.go
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config
|
||||
core-run: fmt vet manifests
|
||||
go run ./cmd/core/main.go
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config with debug logs
|
||||
core-debug-run: fmt vet manifests
|
||||
go run ./cmd/core/main.go --log-debug=true
|
||||
|
||||
# Install CRDs and Definitions of Vela Core into a cluster, this is for develop convenient.
|
||||
core-install: manifests
|
||||
kubectl apply -f hack/namespace.yaml
|
||||
kubectl apply -f charts/vela-core/crds/
|
||||
@$(OK) install succeed
|
||||
|
||||
# Uninstall CRDs and Definitions of Vela Core from a cluster, this is for develop convenient.
|
||||
core-uninstall: manifests
|
||||
kubectl delete -f charts/vela-core/crds/
|
||||
|
||||
# Generate manifests e.g. CRD, RBAC etc.
|
||||
manifests: installcue kustomize
|
||||
go generate $(foreach t,pkg apis,./$(t)/...)
|
||||
@@ -295,82 +112,13 @@ manifests: installcue kustomize
|
||||
rm -f config/crd/base/*
|
||||
./vela-templates/gen_definitions.sh
|
||||
|
||||
GOLANGCILINT_VERSION ?= v1.38.0
|
||||
|
||||
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]')
|
||||
HOSTARCH := $(shell uname -m)
|
||||
ifeq ($(HOSTARCH),x86_64)
|
||||
HOSTARCH := amd64
|
||||
endif
|
||||
|
||||
golangci:
|
||||
ifneq ($(shell which golangci-lint),)
|
||||
@$(OK) golangci-lint is already installed
|
||||
GOLANGCILINT=$(shell which golangci-lint)
|
||||
else ifeq (, $(shell which $(GOBIN)/golangci-lint))
|
||||
@{ \
|
||||
set -e ;\
|
||||
echo 'installing golangci-lint-$(GOLANGCILINT_VERSION)' ;\
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) $(GOLANGCILINT_VERSION) ;\
|
||||
echo 'Successfully installed' ;\
|
||||
}
|
||||
GOLANGCILINT=$(GOBIN)/golangci-lint
|
||||
else
|
||||
@$(OK) golangci-lint is already installed
|
||||
GOLANGCILINT=$(GOBIN)/golangci-lint
|
||||
endif
|
||||
|
||||
.PHONY: staticchecktool
|
||||
staticchecktool:
|
||||
ifeq (, $(shell which staticcheck))
|
||||
@{ \
|
||||
set -e ;\
|
||||
echo 'installing honnef.co/go/tools/cmd/staticcheck ' ;\
|
||||
GO111MODULE=off go get honnef.co/go/tools/cmd/staticcheck ;\
|
||||
}
|
||||
STATICCHECK=$(GOBIN)/staticcheck
|
||||
else
|
||||
STATICCHECK=$(shell which staticcheck)
|
||||
endif
|
||||
|
||||
.PHONY: goimports
|
||||
goimports:
|
||||
ifeq (, $(shell which goimports))
|
||||
@{ \
|
||||
set -e ;\
|
||||
GO111MODULE=off go get -u golang.org/x/tools/cmd/goimports ;\
|
||||
}
|
||||
GOIMPORTS=$(GOBIN)/goimports
|
||||
else
|
||||
GOIMPORTS=$(shell which goimports)
|
||||
endif
|
||||
|
||||
.PHONY: installcue
|
||||
installcue:
|
||||
ifeq (, $(shell which cue))
|
||||
@{ \
|
||||
set -e ;\
|
||||
GO111MODULE=off go get -u cuelang.org/go/cmd/cue ;\
|
||||
}
|
||||
CUE=$(GOBIN)/cue
|
||||
else
|
||||
CUE=$(shell which cue)
|
||||
endif
|
||||
|
||||
KUSTOMIZE_VERSION ?= 3.8.2
|
||||
|
||||
.PHONY: kustomize
|
||||
kustomize:
|
||||
ifeq (, $(shell kustomize version | grep $(KUSTOMIZE_VERSION)))
|
||||
@{ \
|
||||
set -eo pipefail ;\
|
||||
echo 'installing kustomize-v$(KUSTOMIZE_VERSION) into $(GOBIN)' ;\
|
||||
curl -sS https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash -s $(KUSTOMIZE_VERSION) $(GOBIN);\
|
||||
echo 'Install succeed' ;\
|
||||
}
|
||||
KUSTOMIZE=$(GOBIN)/kustomize
|
||||
else
|
||||
KUSTOMIZE=$(shell which kustomize)
|
||||
endif
|
||||
|
||||
check-license-header:
|
||||
./hack/licence/header-check.sh
|
||||
|
||||
@@ -64,8 +64,7 @@ type Config map[string]string
|
||||
type EnvMeta struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
Current string `json:"current,omitempty"`
|
||||
Current string `json:"current"`
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -83,6 +82,8 @@ const (
|
||||
TypeDefinition = "Managing Definitions"
|
||||
// TypePlugin defines one category used in Kubectl Plugin
|
||||
TypePlugin = "Plugin Command"
|
||||
// TypeUISchema defines one category
|
||||
TypeUISchema = "Managing UISchema"
|
||||
)
|
||||
|
||||
// LabelArg is the argument `label` of a definition
|
||||
|
||||
@@ -1 +1,29 @@
|
||||
Welcome to use the KubeVela! Enjoy your shipping application journey!
|
||||
Welcome to use the KubeVela! Enjoy your shipping application journey!
|
||||
|
||||
,
|
||||
//,
|
||||
////
|
||||
./ /////*
|
||||
,/// ///////
|
||||
.///// ////////
|
||||
/////// /////////
|
||||
//////// //////////
|
||||
,///////// ///////////
|
||||
,////////// ///////////.
|
||||
./////////// ////////////
|
||||
//////////// ////////////.
|
||||
*//////////// ////////////*
|
||||
#@@@@@@@@@@@* ..,,***/ /////////////
|
||||
/@@@@@@@@@@@#
|
||||
*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&
|
||||
.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.
|
||||
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
.&@@@* *@@@& ,@@@&.
|
||||
|
||||
_ __ _ __ __ _
|
||||
| |/ /_ _ | |__ ___\ \ / /___ | | __ _
|
||||
| ' /| | | || '_ \ / _ \\ \ / // _ \| | / _` |
|
||||
| . \| |_| || |_) || __/ \ V /| __/| || (_| |
|
||||
|_|\_\\__,_||_.__/ \___| \_/ \___||_| \__,_|
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{{ if .Values.multicluster.enabled }}
|
||||
{{ if not (lookup "apps/v1" "Deployment" .Release.Namespace (printf "%s-cluster-gateway" .Release.Name)) }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -73,7 +72,6 @@ spec:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 1
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
---
|
||||
{{ if .Values.multicluster.enabled }}
|
||||
apiVersion: v1
|
||||
@@ -91,7 +89,10 @@ spec:
|
||||
{{ end }}
|
||||
---
|
||||
{{ if .Values.multicluster.enabled }}
|
||||
{{ if not (lookup "apiregistration.k8s.io/v1" "APIService" "" "v1alpha1.cluster.core.oam.dev") }}
|
||||
{{ $apiSvc := (lookup "apiregistration.k8s.io/v1" "APIService" "" "v1alpha1.cluster.core.oam.dev") }}
|
||||
{{ $shouldAdopt := (not $apiSvc) }}
|
||||
{{ if not $shouldAdopt }}{{ $shouldAdopt = (index ($apiSvc).metadata.annotations "meta.helm.sh/release-name") }}{{ end }}
|
||||
{{ if $shouldAdopt }}
|
||||
apiVersion: apiregistration.k8s.io/v1
|
||||
kind: APIService
|
||||
metadata:
|
||||
|
||||
@@ -19,7 +19,16 @@ spec:
|
||||
parameter: {
|
||||
dingding?: {
|
||||
// +usage=Specify the the dingding url, you can either sepcify it in value or use secretRef
|
||||
url: value | secretRef
|
||||
url: {
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
message: {
|
||||
text?: *null | {
|
||||
@@ -66,7 +75,16 @@ spec:
|
||||
|
||||
slack?: {
|
||||
// +usage=Specify the the slack url, you can either sepcify it in value or use secretRef
|
||||
url: value | secretRef
|
||||
url: {
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
message: {
|
||||
text: string
|
||||
@@ -88,7 +106,16 @@ spec:
|
||||
// +usage=The alias is the email alias to show after sending the email
|
||||
alias?: string
|
||||
// +usage=Specify the password of the email, you can either sepcify it in value or use secretRef
|
||||
password: value | secretRef
|
||||
password: {
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +usage=Specify the host of your email
|
||||
host: string
|
||||
// +usage=Specify the port of the email host, default to 587
|
||||
@@ -150,13 +177,6 @@ spec:
|
||||
description?: textType
|
||||
url?: string
|
||||
}
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
value: string
|
||||
// send webhook notification
|
||||
ding: op.#Steps & {
|
||||
if parameter.dingding != _|_ {
|
||||
|
||||
@@ -68,14 +68,18 @@ spec:
|
||||
} @step(8)
|
||||
}
|
||||
}
|
||||
secretRef: {
|
||||
name: string
|
||||
key: string
|
||||
}
|
||||
value: string
|
||||
parameter: {
|
||||
// +usage=Specify the webhook url
|
||||
url: value | secretRef
|
||||
url: {
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +usage=Specify the data you want to send
|
||||
data?: {...}
|
||||
}
|
||||
|
||||
@@ -494,7 +494,12 @@ spec:
|
||||
replica: strconv.FormatInt(context.output.status.readyReplicas, 10)
|
||||
}
|
||||
}
|
||||
message: "Ready:" + ready.replica + "/" + strconv.FormatInt(context.output.status.replicas, 10)
|
||||
if context.output.status.replicas != _|_ {
|
||||
message: "Ready:" + ready.replica + "/" + strconv.FormatInt(context.output.status.replicas, 10)
|
||||
}
|
||||
if context.output.status.replicas == _|_ {
|
||||
message: ""
|
||||
}
|
||||
healthPolicy: |-
|
||||
ready: {
|
||||
if context.output.status.readyReplicas == _|_ {
|
||||
@@ -504,7 +509,12 @@ spec:
|
||||
replica: context.output.status.readyReplicas
|
||||
}
|
||||
}
|
||||
isHealth: context.output.status.replicas == context.output.status.readyReplicas
|
||||
if context.output.status.replicas != _|_ {
|
||||
isHealth: context.output.status.replicas == ready.replica
|
||||
}
|
||||
if context.output.status.replicas == _|_ {
|
||||
isHealth: false
|
||||
}
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
|
||||
@@ -405,7 +405,12 @@ spec:
|
||||
replica: strconv.FormatInt(context.output.status.readyReplicas, 10)
|
||||
}
|
||||
}
|
||||
message: "Ready:" + ready.replica + "/" + strconv.FormatInt(context.output.status.replicas, 10)
|
||||
if context.output.status.replicas != _|_ {
|
||||
message: "Ready:" + ready.replica + "/" + strconv.FormatInt(context.output.status.replicas, 10)
|
||||
}
|
||||
if context.output.status.replicas == _|_ {
|
||||
message: ""
|
||||
}
|
||||
healthPolicy: |-
|
||||
ready: {
|
||||
if context.output.status.readyReplicas == _|_ {
|
||||
@@ -415,7 +420,12 @@ spec:
|
||||
replica: context.output.status.readyReplicas
|
||||
}
|
||||
}
|
||||
isHealth: context.output.status.replicas == context.output.status.readyReplicas
|
||||
if context.output.status.replicas != _|_ {
|
||||
isHealth: context.output.status.replicas == ready.replica
|
||||
}
|
||||
if context.output.status.replicas == _|_ {
|
||||
isHealth: false
|
||||
}
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
|
||||
@@ -19,7 +19,16 @@ spec:
|
||||
parameter: {
|
||||
dingding?: {
|
||||
// +usage=Specify the the dingding url, you can either sepcify it in value or use secretRef
|
||||
url: value | secretRef
|
||||
url: {
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
message: {
|
||||
text?: *null | {
|
||||
@@ -66,7 +75,16 @@ spec:
|
||||
|
||||
slack?: {
|
||||
// +usage=Specify the the slack url, you can either sepcify it in value or use secretRef
|
||||
url: value | secretRef
|
||||
url: {
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +useage=Specify the message that you want to sent
|
||||
message: {
|
||||
text: string
|
||||
@@ -88,7 +106,16 @@ spec:
|
||||
// +usage=The alias is the email alias to show after sending the email
|
||||
alias?: string
|
||||
// +usage=Specify the password of the email, you can either sepcify it in value or use secretRef
|
||||
password: value | secretRef
|
||||
password: {
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +usage=Specify the host of your email
|
||||
host: string
|
||||
// +usage=Specify the port of the email host, default to 587
|
||||
@@ -150,13 +177,6 @@ spec:
|
||||
description?: textType
|
||||
url?: string
|
||||
}
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
value: string
|
||||
// send webhook notification
|
||||
ding: op.#Steps & {
|
||||
if parameter.dingding != _|_ {
|
||||
|
||||
@@ -68,14 +68,18 @@ spec:
|
||||
} @step(8)
|
||||
}
|
||||
}
|
||||
secretRef: {
|
||||
name: string
|
||||
key: string
|
||||
}
|
||||
value: string
|
||||
parameter: {
|
||||
// +usage=Specify the webhook url
|
||||
url: value | secretRef
|
||||
url: {
|
||||
value: string
|
||||
} | {
|
||||
secretRef: {
|
||||
// +usage=name is the name of the secret
|
||||
name: string
|
||||
// +usage=key is the key in the secret
|
||||
key: string
|
||||
}
|
||||
}
|
||||
// +usage=Specify the data you want to send
|
||||
data?: {...}
|
||||
}
|
||||
|
||||
@@ -494,7 +494,12 @@ spec:
|
||||
replica: strconv.FormatInt(context.output.status.readyReplicas, 10)
|
||||
}
|
||||
}
|
||||
message: "Ready:" + ready.replica + "/" + strconv.FormatInt(context.output.status.replicas, 10)
|
||||
if context.output.status.replicas != _|_ {
|
||||
message: "Ready:" + ready.replica + "/" + strconv.FormatInt(context.output.status.replicas, 10)
|
||||
}
|
||||
if context.output.status.replicas == _|_ {
|
||||
message: ""
|
||||
}
|
||||
healthPolicy: |-
|
||||
ready: {
|
||||
if context.output.status.readyReplicas == _|_ {
|
||||
@@ -504,7 +509,12 @@ spec:
|
||||
replica: context.output.status.readyReplicas
|
||||
}
|
||||
}
|
||||
isHealth: context.output.status.replicas == context.output.status.readyReplicas
|
||||
if context.output.status.replicas != _|_ {
|
||||
isHealth: context.output.status.replicas == ready.replica
|
||||
}
|
||||
if context.output.status.replicas == _|_ {
|
||||
isHealth: false
|
||||
}
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
|
||||
@@ -405,7 +405,12 @@ spec:
|
||||
replica: strconv.FormatInt(context.output.status.readyReplicas, 10)
|
||||
}
|
||||
}
|
||||
message: "Ready:" + ready.replica + "/" + strconv.FormatInt(context.output.status.replicas, 10)
|
||||
if context.output.status.replicas != _|_ {
|
||||
message: "Ready:" + ready.replica + "/" + strconv.FormatInt(context.output.status.replicas, 10)
|
||||
}
|
||||
if context.output.status.replicas == _|_ {
|
||||
message: ""
|
||||
}
|
||||
healthPolicy: |-
|
||||
ready: {
|
||||
if context.output.status.readyReplicas == _|_ {
|
||||
@@ -415,7 +420,12 @@ spec:
|
||||
replica: context.output.status.readyReplicas
|
||||
}
|
||||
}
|
||||
isHealth: context.output.status.replicas == context.output.status.readyReplicas
|
||||
if context.output.status.replicas != _|_ {
|
||||
isHealth: context.output.status.replicas == ready.replica
|
||||
}
|
||||
if context.output.status.replicas == _|_ {
|
||||
isHealth: false
|
||||
}
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
|
||||
@@ -135,27 +135,52 @@ Start to test.
|
||||
make e2e-test
|
||||
```
|
||||
|
||||
## Contribute apiserver and [velaux](https://github.com/oam-dev/velaux)
|
||||
|
||||
Before start, please make sure you have already started the vela controller environment.
|
||||
|
||||
```shell
|
||||
make run-apiserver
|
||||
```
|
||||
|
||||
By default, the apiserver will serving at "0.0.0.0:8000".
|
||||
|
||||
Get the velaux code by:
|
||||
|
||||
```shell
|
||||
git clone git@github.com:oam-dev/velaux.git
|
||||
```
|
||||
|
||||
Configure the apiserver address:
|
||||
|
||||
```shell
|
||||
cd velaux
|
||||
echo "BASE_DOMAIN='http://127.0.0.1:8000'" > .env
|
||||
```
|
||||
|
||||
Make sure you have installed [yarn](https://classic.yarnpkg.com/en/docs/install).
|
||||
|
||||
```shell
|
||||
yarn install
|
||||
yarn start
|
||||
```
|
||||
|
||||
To execute the e2e test of the API module, the mongodb service needs to exist locally.
|
||||
|
||||
```shell script
|
||||
# save your config
|
||||
mv ~/.kube/config ~/.kube/config.save
|
||||
|
||||
kind create cluster --image kindest/node:v1.18.15@sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4 --name worker
|
||||
kind get kubeconfig --name worker --internal > /tmp/worker.kubeconfig
|
||||
kind get kubeconfig --name worker > /tmp/worker.client.kubeconfig
|
||||
|
||||
# restore your config
|
||||
mv ~/.kube/config.save ~/.kube/config
|
||||
|
||||
make e2e-apiserver-test
|
||||
```
|
||||
|
||||
## Contribute Docs
|
||||
|
||||
Please read [the documentation](https://github.com/oam-dev/kubevela/tree/master/docs/README.md) before contributing to the docs.
|
||||
|
||||
- Build docs
|
||||
|
||||
```shell script
|
||||
make docs-build
|
||||
```
|
||||
|
||||
- Local development and preview
|
||||
|
||||
```shell script
|
||||
make docs-start
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,46 +10,69 @@ spec:
|
||||
import ("vela/op")
|
||||
|
||||
parameter: {
|
||||
repoUrl: string
|
||||
dockerfile: string
|
||||
image: string
|
||||
// +usage=Specify the SSH URL of git repository for your source codes, e.g. git@git.woa.com:my-git-repo/demo.git
|
||||
repoUrl: string
|
||||
|
||||
// +usage=Specify the branch which you want to be checked out and built
|
||||
branch: string
|
||||
|
||||
// +usage=Specify the filename of Dockerfile under the root folder in your git repository
|
||||
dockerfile: string
|
||||
|
||||
// +usage=Specify the secret name for the docker repository in your namespace in your K8S cluster
|
||||
dockerSecret: string
|
||||
|
||||
// +usage=Specify the address where the image built is pushed into
|
||||
image: string
|
||||
}
|
||||
|
||||
let pushImage=image
|
||||
|
||||
build: op.#Task & {
|
||||
name: "\(context.name)-\(context.stepId)"
|
||||
namespace: context.namespace
|
||||
name: "\(context.name)-build-\(context.stepSessionID)"
|
||||
namespace: context.namespace
|
||||
|
||||
// Declare workspaces that used to share data.
|
||||
workspace: {
|
||||
"code-dir": {}
|
||||
}
|
||||
toolImage: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/entrypoint:v0.27.2"
|
||||
baseImage: "busybox:1.34"
|
||||
|
||||
steps: [
|
||||
// git clone code repo.
|
||||
{
|
||||
name: "git-clone"
|
||||
image: "git-image-with-pull-ssh"
|
||||
workspaceMounts: [{workspace: workspace["code-dir"], mountPath: "/data/code"}]
|
||||
script: """
|
||||
#! /bin/bash
|
||||
git clone \(repourl) /data/code
|
||||
"""
|
||||
},
|
||||
// build and push image.
|
||||
{
|
||||
name: "build-image"
|
||||
image: "gcr.io/kaniko-project/executor:v1.3.0"
|
||||
workspaceMounts: [{workspace: workspace["code-dir"], mountPath: "/data/build"}]
|
||||
script: """
|
||||
#! /bin/bash
|
||||
/kaniko/executor --dockerfile=/data/build/\(dockerfile) --context=/data/build --destination=\(pushImage)
|
||||
"""
|
||||
}]
|
||||
// Declare workspaces that used to share data.
|
||||
workspaces: {
|
||||
"code-dir": {}
|
||||
}
|
||||
|
||||
secrets: {
|
||||
"\(parameter.dockerSecret)": {
|
||||
items: [{
|
||||
key: ".dockerconfigjson"
|
||||
path: "config.json"
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
steps: [
|
||||
// git clone code repo.
|
||||
{
|
||||
name: "git-clone"
|
||||
image: "bitnami/git:2.34.1"
|
||||
workspaceMounts: [{workspace: workspaces["code-dir"], mountPath: "/data/code"}]
|
||||
script: """
|
||||
#!/bin/bash
|
||||
git clone -b \(parameter.branch) \(parameter.repoUrl) /data/code
|
||||
"""
|
||||
},
|
||||
// build and push image.
|
||||
{
|
||||
name: "build-image"
|
||||
image: "gcr.io/kaniko-project/executor:debug"
|
||||
workspaceMounts: [{workspace: workspaces["code-dir"], mountPath: "/data/build"}]
|
||||
secretMounts: [{secret: secrets["\(parameter.dockerSecret)"], mountPath: "/kaniko/.docker/"}]
|
||||
script: """
|
||||
#!/busybox/sh
|
||||
/kaniko/executor --dockerfile=/data/build/\(parameter.dockerfile) --context=/data/build --destination=\(parameter.image)
|
||||
"""
|
||||
}]
|
||||
}
|
||||
|
||||
// wait until deployment ready
|
||||
wait: op.#ConditionalWait & {
|
||||
continue: build.do.status.phase == "successed"
|
||||
continue: build.apply.value.status.phase == "Succeeded"
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,12 @@ var _ = Describe("Addon Test", func() {
|
||||
Expect(output).To(ContainSubstring("Successfully enable addon"))
|
||||
})
|
||||
|
||||
It("Upgrade addon test-addon", func() {
|
||||
output, err := e2e.Exec("vela addon upgrade test-addon")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).To(ContainSubstring("Successfully enable addon"))
|
||||
})
|
||||
|
||||
It("Disable addon test-addon", func() {
|
||||
output, err := e2e.LongTimeExec("vela addon disable test-addon", 600*time.Second)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -90,12 +96,12 @@ var _ = Describe("Addon Test", func() {
|
||||
})
|
||||
|
||||
It("Add test addon registry", func() {
|
||||
output, err := e2e.LongTimeExec("vela addon registry add my-repo --type=git --gitUrl=https://github.com/oam-dev/catalog --path=/experimental/addons", 600*time.Second)
|
||||
output, err := e2e.LongTimeExec("vela addon registry add my-repo --type=git --endpoint=https://github.com/oam-dev/catalog --path=/experimental/addons", 600*time.Second)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).To(ContainSubstring("Successfully add an addon registry my-repo"))
|
||||
|
||||
Eventually(func() error {
|
||||
output, err := e2e.LongTimeExec("vela addon registry update my-repo --type=git --gitUrl=https://github.com/oam-dev/catalog --path=/addons", 300*time.Second)
|
||||
output, err := e2e.LongTimeExec("vela addon registry update my-repo --type=git --endpoint=https://github.com/oam-dev/catalog --path=/addons", 300*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -46,9 +46,9 @@ var (
|
||||
|
||||
var _ = ginkgo.Describe("Test Vela Application", func() {
|
||||
e2e.JsonAppFileContext("json appfile apply", jsonAppFile)
|
||||
e2e.EnvSetContext("env set", "default")
|
||||
e2e.EnvSetContext("env set default", "default")
|
||||
e2e.DeleteEnvFunc("env delete", envName)
|
||||
e2e.EnvInitContext("env init", envName)
|
||||
e2e.EnvInitContext("env init env-application", envName)
|
||||
e2e.EnvSetContext("env set", envName)
|
||||
e2e.JsonAppFileContext("deploy app-basic", appbasicJsonAppFile)
|
||||
ApplicationExecContext("exec -- COMMAND", applicationName)
|
||||
@@ -185,7 +185,7 @@ var ApplicationDeleteWithWaitOptions = func(context string, appName string) bool
|
||||
cli := fmt.Sprintf("vela delete %s --wait", appName)
|
||||
output, err := e2e.ExecAndTerminate(cli)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("deleted from namespace"))
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("deleted"))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -225,7 +225,7 @@ var ApplicationDeleteWithForceOptions = func(context string, appName string) boo
|
||||
cli = fmt.Sprintf("vela delete %s --force", appName)
|
||||
output, err = e2e.ExecAndTerminate(cli)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("deleted from namespace"))
|
||||
gomega.Expect(output).To(gomega.ContainSubstring("deleted"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ createTime: "0001-01-01T00:00:00Z"
|
||||
name: initmyapp
|
||||
services:
|
||||
mysvc:
|
||||
addRevisionLabel: false
|
||||
cpu: "0.5"
|
||||
image: nginx:latest
|
||||
imagePullPolicy: Always
|
||||
memory: 200M
|
||||
port: 80
|
||||
type: webservice
|
||||
updateTime: "0001-01-01T00:00:00Z"
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Netflix/go-expect"
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/onsi/gomega"
|
||||
)
|
||||
@@ -31,9 +32,29 @@ var (
|
||||
return ginkgo.Context(context, func() {
|
||||
ginkgo.It("should print environment initiation successful message", func() {
|
||||
cli := fmt.Sprintf("vela env init %s", envName)
|
||||
output, err := Exec(cli)
|
||||
var answer = "default"
|
||||
if envName != "env-application" {
|
||||
answer = "vela-system"
|
||||
}
|
||||
output, err := InteractiveExec(cli, func(c *expect.Console) {
|
||||
data := []struct {
|
||||
q, a string
|
||||
}{
|
||||
{
|
||||
q: "Would you like to choose an existing namespaces as your env?",
|
||||
a: answer,
|
||||
},
|
||||
}
|
||||
for _, qa := range data {
|
||||
_, err := c.ExpectString(qa.q)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
_, err = c.SendLine(qa.a)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
}
|
||||
c.ExpectEOF()
|
||||
})
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
expectedOutput := fmt.Sprintf("environment %s created", envName)
|
||||
expectedOutput := fmt.Sprintf("environment %s with namespace %s created", envName, answer)
|
||||
gomega.Expect(output).To(gomega.ContainSubstring(expectedOutput))
|
||||
})
|
||||
})
|
||||
@@ -45,7 +66,7 @@ var (
|
||||
cli := fmt.Sprintf("vela env init %s --namespace %s", envName, namespace)
|
||||
output, err := Exec(cli)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
expectedOutput := fmt.Sprintf("environment %s created", envName)
|
||||
expectedOutput := fmt.Sprintf("environment %s with namespace %s created", envName, namespace)
|
||||
gomega.Expect(output).To(gomega.ContainSubstring(expectedOutput))
|
||||
})
|
||||
})
|
||||
@@ -92,8 +113,7 @@ var (
|
||||
cli := fmt.Sprintf("vela env sw %s", envName)
|
||||
output, err := Exec(cli)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
expectedOutput := fmt.Sprintf("Set environment succeed, current environment is %s", envName)
|
||||
gomega.Expect(output).To(gomega.ContainSubstring(expectedOutput))
|
||||
gomega.Expect(output).To(gomega.ContainSubstring(envName))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -109,17 +129,6 @@ var (
|
||||
})
|
||||
})
|
||||
}
|
||||
EnvDeleteCurrentUsingContext = func(context string, envName string) bool {
|
||||
return ginkgo.Context(context, func() {
|
||||
ginkgo.It("should delete all envs", func() {
|
||||
cli := fmt.Sprintf("vela env delete %s", envName)
|
||||
output, err := Exec(cli)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
expectedOutput := fmt.Sprintf("Error: you can't delete current using environment %s", envName)
|
||||
gomega.Expect(output).To(gomega.ContainSubstring(expectedOutput))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
WorkloadDeleteContext = func(context string, applicationName string) bool {
|
||||
return ginkgo.Context(context, func() {
|
||||
|
||||
5
e2e/env/env_test.go
vendored
5
e2e/env/env_test.go
vendored
@@ -29,8 +29,8 @@ var (
|
||||
)
|
||||
|
||||
var _ = ginkgo.Describe("Env", func() {
|
||||
e2e.EnvInitContext("env init", envName)
|
||||
e2e.EnvInitContext("env init another one", envName2)
|
||||
e2e.EnvInitWithNamespaceOptionContext("env init env-hello --namespace heelo", envName, "heelo")
|
||||
e2e.EnvInitWithNamespaceOptionContext("env init another one --namespace heelo2", envName2, "heelo2")
|
||||
e2e.EnvShowContext("env show", envName)
|
||||
e2e.EnvSetContext("env set", envName)
|
||||
|
||||
@@ -46,5 +46,4 @@ var _ = ginkgo.Describe("Env", func() {
|
||||
})
|
||||
|
||||
e2e.EnvDeleteContext("env delete", envName2)
|
||||
e2e.EnvDeleteCurrentUsingContext("env delete currently using one", envName)
|
||||
})
|
||||
|
||||
26
makefiles/build.mk
Normal file
26
makefiles/build.mk
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
.PHONY: vela-cli
|
||||
vela-cli:
|
||||
$(GOBUILD_ENV) go build -o bin/vela -a -ldflags $(LDFLAGS) ./references/cmd/cli/main.go
|
||||
|
||||
.PHONY: kubectl-vela
|
||||
kubectl-vela:
|
||||
$(GOBUILD_ENV) go build -o bin/kubectl-vela -a -ldflags $(LDFLAGS) ./cmd/plugin/main.go
|
||||
|
||||
# Build the docker image
|
||||
.PHONY: docker-build
|
||||
docker-build: docker-build-core docker-build-apiserver
|
||||
@$(OK)
|
||||
|
||||
.PHONY: docker-build-core
|
||||
docker-build-core:
|
||||
docker build --build-arg=VERSION=$(VELA_VERSION) --build-arg=GITVERSION=$(GIT_COMMIT) -t $(VELA_CORE_IMAGE) .
|
||||
|
||||
.PHONY: docker-build-apiserver
|
||||
docker-build-apiserver:
|
||||
docker build --build-arg=VERSION=$(VELA_VERSION) --build-arg=GITVERSION=$(GIT_COMMIT) -t $(VELA_APISERVER_IMAGE) -f Dockerfile.apiserver .
|
||||
|
||||
# Build the runtime docker image
|
||||
.PHONY: docker-build-runtime-rollout
|
||||
docker-build-runtime-rollout:
|
||||
docker build --build-arg=VERSION=$(VELA_VERSION) --build-arg=GITVERSION=$(GIT_COMMIT) -t $(VELA_RUNTIME_ROLLOUT_IMAGE) -f runtime/rollout/Dockerfile .
|
||||
51
makefiles/const.mk
Normal file
51
makefiles/const.mk
Normal file
@@ -0,0 +1,51 @@
|
||||
SHELL := /bin/bash
|
||||
|
||||
GOBUILD_ENV = GO111MODULE=on CGO_ENABLED=0
|
||||
GOX = go run github.com/mitchellh/gox
|
||||
TARGETS := darwin/amd64 linux/amd64 windows/amd64
|
||||
DIST_DIRS := find * -type d -exec
|
||||
|
||||
TIME_LONG = `date +%Y-%m-%d' '%H:%M:%S`
|
||||
TIME_SHORT = `date +%H:%M:%S`
|
||||
TIME = $(TIME_SHORT)
|
||||
|
||||
BLUE := $(shell printf "\033[34m")
|
||||
YELLOW := $(shell printf "\033[33m")
|
||||
RED := $(shell printf "\033[31m")
|
||||
GREEN := $(shell printf "\033[32m")
|
||||
CNone := $(shell printf "\033[0m")
|
||||
|
||||
INFO = echo ${TIME} ${BLUE}[ .. ]${CNone}
|
||||
WARN = echo ${TIME} ${YELLOW}[WARN]${CNone}
|
||||
ERR = echo ${TIME} ${RED}[FAIL]${CNone}
|
||||
OK = echo ${TIME} ${GREEN}[ OK ]${CNone}
|
||||
FAIL = (echo ${TIME} ${RED}[FAIL]${CNone} && false)
|
||||
|
||||
|
||||
|
||||
# Vela version
|
||||
VELA_VERSION ?= master
|
||||
# Repo info
|
||||
GIT_COMMIT ?= git-$(shell git rev-parse --short HEAD)
|
||||
GIT_COMMIT_LONG ?= $(shell git rev-parse HEAD)
|
||||
VELA_VERSION_KEY := github.com/oam-dev/kubevela/version.VelaVersion
|
||||
VELA_GITVERSION_KEY := github.com/oam-dev/kubevela/version.GitRevision
|
||||
LDFLAGS ?= "-s -w -X $(VELA_VERSION_KEY)=$(VELA_VERSION) -X $(VELA_GITVERSION_KEY)=$(GIT_COMMIT)"
|
||||
|
||||
|
||||
|
||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||
ifeq (,$(shell go env GOBIN))
|
||||
GOBIN=$(shell go env GOPATH)/bin
|
||||
else
|
||||
GOBIN=$(shell go env GOBIN)
|
||||
endif
|
||||
|
||||
# Image URL to use all building/pushing image targets
|
||||
VELA_CORE_IMAGE ?= vela-core:latest
|
||||
VELA_CORE_TEST_IMAGE ?= vela-core-test:$(GIT_COMMIT)
|
||||
VELA_APISERVER_IMAGE ?= apiserver:latest
|
||||
VELA_RUNTIME_ROLLOUT_IMAGE ?= vela-runtime-rollout:latest
|
||||
VELA_RUNTIME_ROLLOUT_TEST_IMAGE ?= vela-runtime-rollout-test:$(GIT_COMMIT)
|
||||
RUNTIME_CLUSTER_CONFIG ?= /tmp/worker.kubeconfig
|
||||
RUNTIME_CLUSTER_NAME ?= worker
|
||||
73
makefiles/dependency.mk
Normal file
73
makefiles/dependency.mk
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
GOLANGCILINT_VERSION ?= v1.38.0
|
||||
|
||||
.PHONY: golangci
|
||||
golangci:
|
||||
ifneq ($(shell which golangci-lint),)
|
||||
@$(OK) golangci-lint is already installed
|
||||
GOLANGCILINT=$(shell which golangci-lint)
|
||||
else ifeq (, $(shell which $(GOBIN)/golangci-lint))
|
||||
@{ \
|
||||
set -e ;\
|
||||
echo 'installing golangci-lint-$(GOLANGCILINT_VERSION)' ;\
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) $(GOLANGCILINT_VERSION) ;\
|
||||
echo 'Successfully installed' ;\
|
||||
}
|
||||
GOLANGCILINT=$(GOBIN)/golangci-lint
|
||||
else
|
||||
@$(OK) golangci-lint is already installed
|
||||
GOLANGCILINT=$(GOBIN)/golangci-lint
|
||||
endif
|
||||
|
||||
.PHONY: staticchecktool
|
||||
staticchecktool:
|
||||
ifeq (, $(shell which staticcheck))
|
||||
@{ \
|
||||
set -e ;\
|
||||
echo 'installing honnef.co/go/tools/cmd/staticcheck ' ;\
|
||||
GO111MODULE=off go get honnef.co/go/tools/cmd/staticcheck ;\
|
||||
}
|
||||
STATICCHECK=$(GOBIN)/staticcheck
|
||||
else
|
||||
STATICCHECK=$(shell which staticcheck)
|
||||
endif
|
||||
|
||||
.PHONY: goimports
|
||||
goimports:
|
||||
ifeq (, $(shell which goimports))
|
||||
@{ \
|
||||
set -e ;\
|
||||
GO111MODULE=off go get -u golang.org/x/tools/cmd/goimports ;\
|
||||
}
|
||||
GOIMPORTS=$(GOBIN)/goimports
|
||||
else
|
||||
GOIMPORTS=$(shell which goimports)
|
||||
endif
|
||||
|
||||
.PHONY: installcue
|
||||
installcue:
|
||||
ifeq (, $(shell which cue))
|
||||
@{ \
|
||||
set -e ;\
|
||||
GO111MODULE=off go get -u cuelang.org/go/cmd/cue ;\
|
||||
}
|
||||
CUE=$(GOBIN)/cue
|
||||
else
|
||||
CUE=$(shell which cue)
|
||||
endif
|
||||
|
||||
KUSTOMIZE_VERSION ?= 3.8.2
|
||||
|
||||
.PHONY: kustomize
|
||||
kustomize:
|
||||
ifeq (, $(shell kustomize version | grep $(KUSTOMIZE_VERSION)))
|
||||
@{ \
|
||||
set -eo pipefail ;\
|
||||
echo 'installing kustomize-v$(KUSTOMIZE_VERSION) into $(GOBIN)' ;\
|
||||
curl -sS https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash -s $(KUSTOMIZE_VERSION) $(GOBIN);\
|
||||
echo 'Install succeed' ;\
|
||||
}
|
||||
KUSTOMIZE=$(GOBIN)/kustomize
|
||||
else
|
||||
KUSTOMIZE=$(shell which kustomize)
|
||||
endif
|
||||
32
makefiles/develop.mk
Normal file
32
makefiles/develop.mk
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
# Run apiserver for velaux(UI)
|
||||
.PHONY: run-apiserver
|
||||
run-apiserver:
|
||||
go run ./cmd/apiserver/main.go
|
||||
|
||||
# Install CRDs and Definitions of Vela Core into a cluster, this is for develop convenient.
|
||||
.PHONY: core-install
|
||||
core-install: manifests
|
||||
kubectl apply -f hack/namespace.yaml
|
||||
kubectl apply -f charts/vela-core/crds/
|
||||
@$(OK) install succeed
|
||||
|
||||
# Uninstall CRDs and Definitions of Vela Core from a cluster, this is for develop convenient.
|
||||
.PHONY: core-uninstall
|
||||
core-uninstall: manifests
|
||||
kubectl delete -f charts/vela-core/crds/
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config
|
||||
.PHONY: run
|
||||
run:
|
||||
go run ./cmd/core/main.go --application-revision-limit 5
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config with debug logs
|
||||
.PHONY: core-debug-run
|
||||
core-debug-run: fmt vet manifests
|
||||
go run ./cmd/core/main.go --log-debug=true
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config
|
||||
.PHONY: core-run
|
||||
core-run: fmt vet manifests
|
||||
go run ./cmd/core/main.go
|
||||
80
makefiles/e2e.mk
Normal file
80
makefiles/e2e.mk
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
.PHONY: e2e-setup-core
|
||||
e2e-setup-core:
|
||||
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
|
||||
kubectl wait --for=condition=Available deployment/kubevela-vela-core -n vela-system --timeout=180s
|
||||
go run ./e2e/addon/mock &
|
||||
|
||||
.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
|
||||
|
||||
.PHONY: e2e-setup
|
||||
e2e-setup:
|
||||
helm install kruise https://github.com/openkruise/kruise/releases/download/v0.9.0/kruise-chart.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 --create-namespace --namespace oam-runtime-system --set image.pullPolicy=IfNotPresent --set image.repository=vela-core-test --set dependCheckWait=10s --set image.tag=$(GIT_COMMIT) --wait oam-runtime ./charts/oam-runtime
|
||||
go run ./e2e/addon/mock &
|
||||
bin/vela addon enable fluxcd
|
||||
bin/vela addon enable terraform
|
||||
bin/vela addon enable terraform-alibaba ALICLOUD_ACCESS_KEY=xxx ALICLOUD_SECRET_KEY=yyy ALICLOUD_REGION=cn-beijing
|
||||
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:
|
||||
# Run e2e test
|
||||
ginkgo -v -skipPackage capability,setup,application -r e2e
|
||||
ginkgo -v -r e2e/application
|
||||
|
||||
ADDONSERVER = $(shell pgrep vela_addon_mock_server)
|
||||
|
||||
|
||||
.PHONY: e2e-apiserver-test
|
||||
e2e-apiserver-test:
|
||||
pkill vela_addon_mock_server || true
|
||||
go run ./e2e/addon/mock/vela_addon_mock_server.go &
|
||||
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_apiserver_test.out ./test/e2e-apiserver-test
|
||||
@$(OK) tests pass
|
||||
|
||||
.PHONY: e2e-test
|
||||
e2e-test:
|
||||
# Run e2e test
|
||||
ginkgo -v --skip="rollout related e2e-test." ./test/e2e-test
|
||||
@$(OK) tests pass
|
||||
|
||||
.PHONY: e2e-addon-test
|
||||
e2e-addon-test:
|
||||
cp bin/vela /tmp/
|
||||
ginkgo -v ./test/e2e-addon-test
|
||||
@$(OK) tests pass
|
||||
|
||||
.PHONY: e2e-rollout-test
|
||||
e2e-rollout-test:
|
||||
ginkgo -v --focus="rollout related e2e-test." ./test/e2e-test
|
||||
@$(OK) tests pass
|
||||
|
||||
.PHONY: e2e-multicluster-test
|
||||
e2e-multicluster-test:
|
||||
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_multicluster_test.out ./test/e2e-multicluster-test
|
||||
@$(OK) tests pass
|
||||
|
||||
.PHONY: e2e-cleanup
|
||||
e2e-cleanup:
|
||||
# Clean up
|
||||
rm -rf ~/.vela
|
||||
|
||||
.PHONY: end-e2e-core
|
||||
end-e2e-core:
|
||||
sh ./hack/e2e/end_e2e_core.sh
|
||||
|
||||
.PHONY: end-e2e
|
||||
end-e2e:
|
||||
sh ./hack/e2e/end_e2e.sh
|
||||
28
makefiles/release.mk
Normal file
28
makefiles/release.mk
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
.PHONY: cross-build
|
||||
cross-build:
|
||||
rm -rf _bin
|
||||
go get github.com/mitchellh/gox@v0.4.0
|
||||
$(GOBUILD_ENV) $(GOX) -ldflags $(LDFLAGS) -parallel=2 -output="_bin/vela/{{.OS}}-{{.Arch}}/vela" -osarch='$(TARGETS)' ./references/cmd/cli
|
||||
$(GOBUILD_ENV) $(GOX) -ldflags $(LDFLAGS) -parallel=2 -output="_bin/kubectl-vela/{{.OS}}-{{.Arch}}/kubectl-vela" -osarch='$(TARGETS)' ./cmd/plugin
|
||||
$(GOBUILD_ENV) $(GOX) -ldflags $(LDFLAGS) -parallel=2 -output="_bin/apiserver/{{.OS}}-{{.Arch}}/apiserver" -osarch="$(TARGETS)" ./cmd/apiserver
|
||||
|
||||
|
||||
.PHONY: compress
|
||||
compress:
|
||||
( \
|
||||
echo "\n## Release Info\nVERSION: $(VELA_VERSION)" >> README.md && \
|
||||
echo "GIT_COMMIT: $(GIT_COMMIT_LONG)\n" >> README.md && \
|
||||
cd _bin/vela && \
|
||||
$(DIST_DIRS) cp ../../LICENSE {} \; && \
|
||||
$(DIST_DIRS) cp ../../README.md {} \; && \
|
||||
$(DIST_DIRS) tar -zcf vela-{}.tar.gz {} \; && \
|
||||
$(DIST_DIRS) zip -r vela-{}.zip {} \; && \
|
||||
cd ../kubectl-vela && \
|
||||
$(DIST_DIRS) cp ../../LICENSE {} \; && \
|
||||
$(DIST_DIRS) cp ../../README.md {} \; && \
|
||||
$(DIST_DIRS) tar -zcf kubectl-vela-{}.tar.gz {} \; && \
|
||||
$(DIST_DIRS) zip -r kubectl-vela-{}.zip {} \; && \
|
||||
cd .. && \
|
||||
sha256sum vela/vela-* kubectl-vela/kubectl-vela-* > sha256sums.txt \
|
||||
)
|
||||
@@ -102,10 +102,21 @@ var (
|
||||
CLIMetaOptions = ListOptions{}
|
||||
)
|
||||
|
||||
const (
|
||||
// ObservabilityAddon is the name of the observability addon
|
||||
ObservabilityAddon = "observability"
|
||||
// ObservabilityAddonEndpointComponent is the endpoint component name of the observability addon
|
||||
ObservabilityAddonEndpointComponent = "grafana"
|
||||
// ObservabilityAddonDomainArg is the domain argument name of the observability addon
|
||||
ObservabilityAddonDomainArg = "domain"
|
||||
)
|
||||
|
||||
// ObservabilityEnvironment contains the Observability addon's domain for each cluster
|
||||
type ObservabilityEnvironment struct {
|
||||
Cluster string
|
||||
Domain string
|
||||
Cluster string
|
||||
Domain string
|
||||
LoadBalancerIP string
|
||||
ServiceExternalIP string
|
||||
}
|
||||
|
||||
// ObservabilityEnvBindingValues is a list of ObservabilityEnvironment and will be used to render observability-env-binding.yaml
|
||||
@@ -125,14 +136,6 @@ const (
|
||||
placement:
|
||||
clusterSelector:
|
||||
name: {{.Cluster}}
|
||||
patch:
|
||||
components:
|
||||
- name: grafana
|
||||
type: helm
|
||||
traits:
|
||||
- type: pure-ingress
|
||||
properties:
|
||||
domain: {{.Domain}}
|
||||
{{ end }}
|
||||
{{ end }}`
|
||||
|
||||
@@ -178,7 +181,7 @@ func GetPatternFromItem(it Item, r AsyncReader, rootPath string) string {
|
||||
}
|
||||
|
||||
// ListAddonUIDataFromReader list addons from AsyncReader
|
||||
func ListAddonUIDataFromReader(r AsyncReader, registryMeta map[string]SourceMeta, opt ListOptions) ([]*UIData, error) {
|
||||
func ListAddonUIDataFromReader(r AsyncReader, registryMeta map[string]SourceMeta, registryName string, opt ListOptions) ([]*UIData, error) {
|
||||
var addons []*UIData
|
||||
var err error
|
||||
var wg sync.WaitGroup
|
||||
@@ -196,6 +199,7 @@ func ListAddonUIDataFromReader(r AsyncReader, registryMeta map[string]SourceMeta
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
addonRes.RegistryName = registryName
|
||||
l.Lock()
|
||||
addons = append(addons, addonRes)
|
||||
l.Unlock()
|
||||
@@ -480,7 +484,7 @@ func RenderApp(ctx context.Context, addon *InstallPackage, config *rest.Config,
|
||||
if err != nil {
|
||||
return nil, ErrRenderCueTmpl
|
||||
}
|
||||
if addon.Name == "observability" && strings.HasSuffix(comp.Name, ".cue") {
|
||||
if addon.Name == ObservabilityAddon && strings.HasSuffix(comp.Name, ".cue") {
|
||||
comp.Name = strings.Split(comp.Name, ".cue")[0]
|
||||
}
|
||||
app.Spec.Components = append(app.Spec.Components, *comp)
|
||||
@@ -500,13 +504,8 @@ func RenderApp(ctx context.Context, addon *InstallPackage, config *rest.Config,
|
||||
Name: "deploy-runtime",
|
||||
Type: "deploy2runtime",
|
||||
})
|
||||
case addon.Name == "observability":
|
||||
arg, ok := args["domain"]
|
||||
if !ok {
|
||||
return nil, ErrorNoDomain
|
||||
}
|
||||
domain := arg.(string)
|
||||
policies, err := preparePolicies4Observability(ctx, k8sClient, domain)
|
||||
case addon.Name == ObservabilityAddon:
|
||||
policies, err := preparePolicies4Observability(ctx, k8sClient)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fail to render the policies for Add-on Observability")
|
||||
}
|
||||
@@ -519,7 +518,7 @@ func RenderApp(ctx context.Context, addon *InstallPackage, config *rest.Config,
|
||||
}},
|
||||
}
|
||||
|
||||
workflowSteps, err := prepareWorkflow4Observability(ctx, k8sClient, domain)
|
||||
workflowSteps, err := prepareWorkflow4Observability(ctx, k8sClient)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fail to prepare the workflow for Add-on Observability")
|
||||
}
|
||||
@@ -607,7 +606,7 @@ func RenderDefinitionSchema(addon *InstallPackage) ([]*unstructured.Unstructured
|
||||
return schemaConfigmaps, nil
|
||||
}
|
||||
|
||||
func allocateDomainForAddon(ctx context.Context, k8sClient client.Client, domain string) ([]ObservabilityEnvironment, error) {
|
||||
func allocateDomainForAddon(ctx context.Context, k8sClient client.Client) ([]ObservabilityEnvironment, error) {
|
||||
secrets, err := multicluster.ListExistingClusterSecrets(ctx, k8sClient)
|
||||
if err != nil {
|
||||
klog.Error(err, "failed to list existing cluster secrets")
|
||||
@@ -618,18 +617,16 @@ func allocateDomainForAddon(ctx context.Context, k8sClient client.Client, domain
|
||||
|
||||
for i, secret := range secrets {
|
||||
cluster := secret.Name
|
||||
domain := fmt.Sprintf("%s.%s", cluster, domain)
|
||||
envs[i] = ObservabilityEnvironment{
|
||||
Cluster: cluster,
|
||||
Domain: domain,
|
||||
}
|
||||
}
|
||||
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
func preparePolicies4Observability(ctx context.Context, k8sClient client.Client, domain string) ([]v1beta1.AppPolicy, error) {
|
||||
clusters, err := allocateDomainForAddon(ctx, k8sClient, domain)
|
||||
func preparePolicies4Observability(ctx context.Context, k8sClient client.Client) ([]v1beta1.AppPolicy, error) {
|
||||
clusters, err := allocateDomainForAddon(ctx, k8sClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -659,8 +656,8 @@ func preparePolicies4Observability(ctx context.Context, k8sClient client.Client,
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
func prepareWorkflow4Observability(ctx context.Context, k8sClient client.Client, domain string) ([]v1beta1.WorkflowStep, error) {
|
||||
clusters, err := allocateDomainForAddon(ctx, k8sClient, domain)
|
||||
func prepareWorkflow4Observability(ctx context.Context, k8sClient client.Client) ([]v1beta1.WorkflowStep, error) {
|
||||
clusters, err := allocateDomainForAddon(ctx, k8sClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -882,7 +879,7 @@ func (h *Installer) loadInstallPackage(name string) (*InstallPackage, error) {
|
||||
|
||||
meta, ok := metas[name]
|
||||
if !ok {
|
||||
return nil, errors.Wrapf(err, "fail to find the addon meta of %s", name)
|
||||
return nil, ErrNotExist
|
||||
}
|
||||
var uiData *UIData
|
||||
uiData, err = h.cache.GetUIData(*h.r, name)
|
||||
|
||||
@@ -34,12 +34,14 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
)
|
||||
|
||||
var paths = []string{
|
||||
@@ -120,7 +122,7 @@ func testReaderFunc(t *testing.T, reader AsyncReader) {
|
||||
assert.True(t, len(uiData.Definitions) > 0)
|
||||
|
||||
// test get ui data
|
||||
uiDataList, err := ListAddonUIDataFromReader(reader, registryMeta, UIMetaOptions)
|
||||
uiDataList, err := ListAddonUIDataFromReader(reader, registryMeta, "KubeVela", UIMetaOptions)
|
||||
assert.True(t, strings.Contains(err.Error(), "#parameter.example: preference mark not allowed at this position"))
|
||||
assert.Equal(t, len(uiDataList), 3)
|
||||
|
||||
@@ -151,11 +153,9 @@ func TestRender(t *testing.T) {
|
||||
envs: []ObservabilityEnvironment{
|
||||
{
|
||||
Cluster: "c1",
|
||||
Domain: "a.com",
|
||||
},
|
||||
{
|
||||
Cluster: "c2",
|
||||
Domain: "b.com",
|
||||
},
|
||||
},
|
||||
tmpl: ObservabilityEnvBindingEnvTmpl,
|
||||
@@ -166,27 +166,11 @@ func TestRender(t *testing.T) {
|
||||
placement:
|
||||
clusterSelector:
|
||||
name: c1
|
||||
patch:
|
||||
components:
|
||||
- name: grafana
|
||||
type: helm
|
||||
traits:
|
||||
- type: pure-ingress
|
||||
properties:
|
||||
domain: a.com
|
||||
|
||||
- name: c2
|
||||
placement:
|
||||
clusterSelector:
|
||||
name: c2
|
||||
patch:
|
||||
components:
|
||||
- name: grafana
|
||||
type: helm
|
||||
traits:
|
||||
- type: pure-ingress
|
||||
properties:
|
||||
domain: b.com
|
||||
|
||||
`,
|
||||
|
||||
@@ -196,11 +180,9 @@ func TestRender(t *testing.T) {
|
||||
envs: []ObservabilityEnvironment{
|
||||
{
|
||||
Cluster: "c1",
|
||||
Domain: "a.com",
|
||||
},
|
||||
{
|
||||
Cluster: "c2",
|
||||
Domain: "b.com",
|
||||
},
|
||||
},
|
||||
tmpl: ObservabilityWorkflow4EnvBindingTmpl,
|
||||
@@ -324,6 +306,76 @@ func TestGetAddonStatus(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddonStatus4Observability(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
addonApplication := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "observability",
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
Status: common.AppStatus{
|
||||
Phase: common.ApplicationRunning,
|
||||
},
|
||||
}
|
||||
|
||||
addonSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: Convert2SecName(ObservabilityAddon),
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
Data: map[string][]byte{},
|
||||
}
|
||||
|
||||
addonService := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
Name: ObservabilityAddonEndpointComponent,
|
||||
},
|
||||
Status: corev1.ServiceStatus{
|
||||
LoadBalancer: corev1.LoadBalancerStatus{
|
||||
Ingress: []corev1.LoadBalancerIngress{
|
||||
{
|
||||
IP: "1.2.3.4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
clusterSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-secret",
|
||||
Labels: map[string]string{
|
||||
v1alpha12.LabelKeyClusterCredentialType: string(v1alpha12.CredentialTypeX509Certificate),
|
||||
},
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"test-key": []byte("test-value"),
|
||||
},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
assert.NoError(t, v1beta1.AddToScheme(scheme))
|
||||
assert.NoError(t, corev1.AddToScheme(scheme))
|
||||
k8sClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(addonApplication, addonSecret).Build()
|
||||
addonStatus, err := GetAddonStatus(context.Background(), k8sClient, ObservabilityAddon)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, addonStatus.AddonPhase, enabling)
|
||||
|
||||
// Addon is not installed in multiple clusters
|
||||
k8sClient = fake.NewClientBuilder().WithScheme(scheme).WithObjects(addonApplication, addonSecret, addonService).Build()
|
||||
addonStatus, err = GetAddonStatus(context.Background(), k8sClient, ObservabilityAddon)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, addonStatus.AddonPhase, enabled)
|
||||
|
||||
// Addon is installed in multiple clusters
|
||||
assert.NoError(t, k8sClient.Create(ctx, clusterSecret))
|
||||
addonStatus, err = GetAddonStatus(context.Background(), k8sClient, ObservabilityAddon)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, addonStatus.AddonPhase, enabled)
|
||||
}
|
||||
|
||||
var baseAddon = InstallPackage{
|
||||
Meta: Meta{
|
||||
Name: "test-render-cue-definition-addon",
|
||||
@@ -383,18 +435,6 @@ func TestRenderApp4Observability(t *testing.T) {
|
||||
},
|
||||
},
|
||||
args: map[string]interface{}{},
|
||||
application: "",
|
||||
err: ErrorNoDomain,
|
||||
},
|
||||
{
|
||||
addon: InstallPackage{
|
||||
Meta: Meta{
|
||||
Name: "observability",
|
||||
},
|
||||
},
|
||||
args: map[string]interface{}{
|
||||
"domain": "a.com",
|
||||
},
|
||||
application: `{"kind":"Application","apiVersion":"core.oam.dev/v1beta1","metadata":{"name":"addon-observability","namespace":"vela-system","creationTimestamp":null,"labels":{"addons.oam.dev/name":"observability"}},"spec":{"components":[],"policies":[{"name":"domain","type":"env-binding","properties":{"envs":null}}],"workflow":{"steps":[{"name":"deploy-control-plane","type":"apply-application-in-parallel"}]}},"status":{}}`,
|
||||
},
|
||||
}
|
||||
@@ -441,10 +481,8 @@ func TestRenderApp4ObservabilityWithK8sData(t *testing.T) {
|
||||
Name: "observability",
|
||||
},
|
||||
},
|
||||
args: map[string]interface{}{
|
||||
"domain": "a.com",
|
||||
},
|
||||
application: `{"kind":"Application","apiVersion":"core.oam.dev/v1beta1","metadata":{"name":"addon-observability","namespace":"vela-system","creationTimestamp":null,"labels":{"addons.oam.dev/name":"observability"}},"spec":{"components":[],"policies":[{"name":"domain","type":"env-binding","properties":{"envs":[{"name":"test-secret","patch":{"components":[{"name":"grafana","traits":[{"properties":{"domain":"test-secret.a.com"},"type":"pure-ingress"}],"type":"helm"}]},"placement":{"clusterSelector":{"name":"test-secret"}}}]}}],"workflow":{"steps":[{"name":"deploy-control-plane","type":"apply-application-in-parallel"},{"name":"test-secret","type":"deploy2env","properties":{"env":"test-secret","parallel":true,"policy":"domain"}}]}},"status":{}}`,
|
||||
args: map[string]interface{}{},
|
||||
application: `{"kind":"Application","apiVersion":"core.oam.dev/v1beta1","metadata":{"name":"addon-observability","namespace":"vela-system","creationTimestamp":null,"labels":{"addons.oam.dev/name":"observability"}},"spec":{"components":[],"policies":[{"name":"domain","type":"env-binding","properties":{"envs":[{"name":"test-secret","placement":{"clusterSelector":{"name":"test-secret"}}}]}}],"workflow":{"steps":[{"name":"deploy-control-plane","type":"apply-application-in-parallel"},{"name":"test-secret","type":"deploy2env","properties":{"env":"test-secret","parallel":true,"policy":"domain"}}]}},"status":{}}`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
|
||||
@@ -18,14 +18,20 @@ package addon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
commontypes "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
commontypes "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
@@ -78,11 +84,46 @@ func GetAddonStatus(ctx context.Context, cli client.Client, name string) (Status
|
||||
}
|
||||
return Status{}, err
|
||||
}
|
||||
|
||||
if app.Status.Workflow != nil && app.Status.Workflow.Suspend {
|
||||
return Status{AddonPhase: suspend, AppStatus: &app.Status}, nil
|
||||
}
|
||||
switch app.Status.Phase {
|
||||
case commontypes.ApplicationRunning:
|
||||
if name == ObservabilityAddon {
|
||||
var (
|
||||
clusters = make(map[string]map[string]interface{})
|
||||
sec v1.Secret
|
||||
domain string
|
||||
)
|
||||
if err = cli.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: Convert2SecName(name)}, &sec); err != nil {
|
||||
klog.ErrorS(err, "failed to get observability secret")
|
||||
return Status{AddonPhase: enabling, AppStatus: &app.Status}, nil
|
||||
}
|
||||
|
||||
if v, ok := sec.Data[ObservabilityAddonDomainArg]; ok {
|
||||
domain = string(v)
|
||||
}
|
||||
observability, err := GetObservabilityAccessibilityInfo(ctx, cli, domain)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to get observability accessibility info")
|
||||
return Status{AddonPhase: enabling, AppStatus: &app.Status}, nil
|
||||
}
|
||||
|
||||
for _, o := range observability {
|
||||
var access = fmt.Sprintf("No loadBalancer found, visiting by using 'vela port-forward %s", ObservabilityAddon)
|
||||
if o.LoadBalancerIP != "" {
|
||||
access = fmt.Sprintf("Visiting URL: %s, IP: %s", o.Domain, o.LoadBalancerIP)
|
||||
}
|
||||
clusters[o.Cluster] = map[string]interface{}{
|
||||
"domain": o.Domain,
|
||||
"loadBalancerIP": o.LoadBalancerIP,
|
||||
"access": access,
|
||||
"serviceExternalIP": o.ServiceExternalIP,
|
||||
}
|
||||
}
|
||||
return Status{AddonPhase: enabled, AppStatus: &app.Status, Clusters: clusters}, nil
|
||||
}
|
||||
return Status{AddonPhase: enabled, AppStatus: &app.Status}, nil
|
||||
case commontypes.ApplicationDeleting:
|
||||
return Status{AddonPhase: disabling, AppStatus: &app.Status}, nil
|
||||
@@ -91,8 +132,60 @@ func GetAddonStatus(ctx context.Context, cli client.Client, name string) (Status
|
||||
}
|
||||
}
|
||||
|
||||
// GetObservabilityAccessibilityInfo will get the accessibility info of addon in local cluster and multiple clusters
|
||||
func GetObservabilityAccessibilityInfo(ctx context.Context, k8sClient client.Client, domain string) ([]ObservabilityEnvironment, error) {
|
||||
domains, err := allocateDomainForAddon(ctx, k8sClient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj := new(unstructured.Unstructured)
|
||||
obj.SetKind("Service")
|
||||
obj.SetAPIVersion("v1")
|
||||
key := client.ObjectKeyFromObject(obj)
|
||||
key.Namespace = types.DefaultKubeVelaNS
|
||||
key.Name = ObservabilityAddonEndpointComponent
|
||||
for i, d := range domains {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
readCtx := multicluster.ContextWithClusterName(ctx, d.Cluster)
|
||||
if err := k8sClient.Get(readCtx, key, obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var svc v1.Service
|
||||
data, err := obj.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(data, &svc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if svc.Status.LoadBalancer.Ingress != nil && len(svc.Status.LoadBalancer.Ingress) == 1 {
|
||||
domains[i].ServiceExternalIP = svc.Status.LoadBalancer.Ingress[0].IP
|
||||
}
|
||||
}
|
||||
// set domain for the cluster if there is no child clusters
|
||||
if len(domains) == 0 {
|
||||
var svc v1.Service
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: ObservabilityAddonEndpointComponent, Namespace: types.DefaultKubeVelaNS}, &svc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if svc.Status.LoadBalancer.Ingress != nil && len(svc.Status.LoadBalancer.Ingress) == 1 {
|
||||
domains = []ObservabilityEnvironment{
|
||||
{
|
||||
ServiceExternalIP: svc.Status.LoadBalancer.Ingress[0].IP,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
// Status contain addon phase and related app status
|
||||
type Status struct {
|
||||
AddonPhase string
|
||||
AppStatus *commontypes.AppStatus
|
||||
// the status of multiple clusters
|
||||
Clusters map[string]map[string]interface{} `json:"clusters,omitempty"`
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ func (r *Registry) ListUIData(registryAddonMeta map[string]SourceMeta, opt ListO
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ListAddonUIDataFromReader(reader, registryAddonMeta, opt)
|
||||
return ListAddonUIDataFromReader(reader, registryAddonMeta, r.Name, opt)
|
||||
}
|
||||
|
||||
// GetInstallPackage get install package which is all needed to enable an addon from addon registry
|
||||
|
||||
@@ -36,6 +36,7 @@ type UIData struct {
|
||||
Definitions []ElementFile `json:"definitions"`
|
||||
CUEDefinitions []ElementFile `json:"CUEDefinitions"`
|
||||
Parameters string `json:"parameters"`
|
||||
RegistryName string `json:"registryName"`
|
||||
}
|
||||
|
||||
// InstallPackage contains all necessary files that can be installed for an addon
|
||||
|
||||
@@ -123,13 +123,13 @@ type ListOptions struct {
|
||||
|
||||
// DataStore datastore interface
|
||||
type DataStore interface {
|
||||
// add entity to database, Name() and TableName() can't return zero value.
|
||||
// Add adds entity to database, Name() and TableName() can't return zero value.
|
||||
Add(ctx context.Context, entity Entity) error
|
||||
|
||||
// batch add entity to database, Name() and TableName() can't return zero value.
|
||||
BatchAdd(ctx context.Context, entitys []Entity) error
|
||||
// BatchAdd will adds batched entities to database, Name() and TableName() can't return zero value.
|
||||
BatchAdd(ctx context.Context, entities []Entity) error
|
||||
|
||||
// Update entity to database, Name() and TableName() can't return zero value.
|
||||
// Put will update entity to database, Name() and TableName() can't return zero value.
|
||||
Put(ctx context.Context, entity Entity) error
|
||||
|
||||
// Delete entity from database, Name() and TableName() can't return zero value.
|
||||
|
||||
@@ -59,7 +59,7 @@ func New(ctx context.Context, cfg datastore.Config) (datastore.DataStore, error)
|
||||
if err := kubeClient.Create(ctx, &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: cfg.Database,
|
||||
Annotations: map[string]string{"description": "For kubevela apiserver metadata storage."},
|
||||
Annotations: map[string]string{"description": "For KubeVela API Server metadata storage."},
|
||||
}}); err != nil {
|
||||
return nil, fmt.Errorf("create namespace failure %w", err)
|
||||
}
|
||||
@@ -247,10 +247,15 @@ func newBySortOptionConfigMap(items []corev1.ConfigMap, sortBy []datastore.SortO
|
||||
data := item.BinaryData["data"]
|
||||
for _, op := range sortBy {
|
||||
res := gjson.Get(string(data), op.Key)
|
||||
if res.Type == gjson.Number {
|
||||
switch res.Type {
|
||||
case gjson.Number:
|
||||
m[op.Key] = res.Num
|
||||
} else {
|
||||
m[op.Key] = res.Raw
|
||||
default:
|
||||
if !res.Time().IsZero() {
|
||||
m[op.Key] = res.Time()
|
||||
} else {
|
||||
m[op.Key] = res.Raw
|
||||
}
|
||||
}
|
||||
}
|
||||
s.objects[i] = m
|
||||
@@ -271,25 +276,28 @@ func (b bySortOptionConfigMap) Less(i, j int) bool {
|
||||
for _, op := range b.sortBy {
|
||||
x := b.objects[i][op.Key]
|
||||
y := b.objects[j][op.Key]
|
||||
_x, xok := x.(float64)
|
||||
_y, yok := y.(float64)
|
||||
var lt, gt bool
|
||||
_x, xok := x.(time.Time)
|
||||
_y, yok := y.(time.Time)
|
||||
var xScore, yScore float64
|
||||
if xok && yok {
|
||||
lt, gt = _x < _y, _x > _y
|
||||
xScore = float64(_x.UnixNano())
|
||||
yScore = float64(_y.UnixNano())
|
||||
}
|
||||
if !xok && !yok {
|
||||
lt, gt = x.(string) < y.(string), x.(string) > y.(string)
|
||||
_x, xok := x.(float64)
|
||||
_y, yok := y.(float64)
|
||||
if xok && yok {
|
||||
xScore = _x
|
||||
yScore = _y
|
||||
}
|
||||
}
|
||||
if xok != yok {
|
||||
lt, gt = false, false
|
||||
}
|
||||
if !lt && !gt {
|
||||
if xScore == yScore {
|
||||
continue
|
||||
}
|
||||
if op.Order == datastore.SortOrderAscending {
|
||||
return lt
|
||||
return xScore < yScore
|
||||
}
|
||||
return gt
|
||||
return xScore > yScore
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -95,14 +95,14 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
It("Test batch add function", func() {
|
||||
var datas = []datastore.Entity{
|
||||
&model.Application{Name: "kubevela-app-2", Description: "this is demo 2"},
|
||||
&model.Application{Namespace: "test-namespace", Name: "kubevela-app-3", Description: "this is demo 3"},
|
||||
&model.Application{Namespace: "test-namespace2", Name: "kubevela-app-4", Description: "this is demo 4"},
|
||||
&model.Application{Name: "kubevela-app-3", Description: "this is demo 3"},
|
||||
&model.Application{Name: "kubevela-app-4", Description: "this is demo 4"},
|
||||
}
|
||||
err := kubeStore.BatchAdd(context.TODO(), datas)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var datas2 = []datastore.Entity{
|
||||
&model.Application{Namespace: "test-namespace", Name: "can-delete", Description: "this is demo can-delete"},
|
||||
&model.Application{Name: "can-delete", Description: "this is demo can-delete"},
|
||||
&model.Application{Name: "kubevela-app-2", Description: "this is demo 2"},
|
||||
}
|
||||
err = kubeStore.BatchAdd(context.TODO(), datas2)
|
||||
@@ -124,17 +124,17 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
})
|
||||
It("Test index", func() {
|
||||
var app = model.Application{
|
||||
Namespace: "test",
|
||||
Name: "test",
|
||||
}
|
||||
selector, err := labels.Parse(fmt.Sprintf("table=%s", app.TableName()))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cmp.Diff(app.Index()["namespace"], "test")).Should(BeEmpty())
|
||||
Expect(cmp.Diff(app.Index()["name"], "test")).Should(BeEmpty())
|
||||
for k, v := range app.Index() {
|
||||
rq, err := labels.NewRequirement(k, selection.Equals, []string{v})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
selector = selector.Add(*rq)
|
||||
}
|
||||
Expect(cmp.Diff(selector.String(), "namespace=test,table=vela_application")).Should(BeEmpty())
|
||||
Expect(cmp.Diff(selector.String(), "name=test,table=vela_application")).Should(BeEmpty())
|
||||
})
|
||||
It("Test list function", func() {
|
||||
var app model.Application
|
||||
@@ -157,12 +157,6 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 4)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
|
||||
app.Namespace = "test-namespace"
|
||||
list, err = kubeStore.List(context.TODO(), &app, nil)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 1)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test list clusters with sort and fuzzy query", func() {
|
||||
@@ -175,14 +169,26 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
Expect(kubeStore.Add(context.TODO(), &model.Cluster{Name: name})).Should(Succeed())
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
entities, err := kubeStore.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{SortBy: []datastore.SortOption{{Key: "model.createTime", Order: datastore.SortOrderAscending}}})
|
||||
entities, err := kubeStore.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderAscending}}})
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(len(entities)).Should(Equal(3))
|
||||
for i, name := range []string{"first", "second", "third"} {
|
||||
Expect(entities[i].(*model.Cluster).Name).Should(Equal(name))
|
||||
}
|
||||
|
||||
entities, err = kubeStore.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{
|
||||
SortBy: []datastore.SortOption{{Key: "model.createTime", Order: datastore.SortOrderDescending}},
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
Page: 1,
|
||||
PageSize: 2,
|
||||
})
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(len(entities)).Should(Equal(2))
|
||||
for i, name := range []string{"third", "second"} {
|
||||
Expect(entities[i].(*model.Cluster).Name).Should(Equal(name))
|
||||
}
|
||||
|
||||
entities, err = kubeStore.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
Page: 2,
|
||||
PageSize: 2,
|
||||
})
|
||||
@@ -192,7 +198,7 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
Expect(entities[i].(*model.Cluster).Name).Should(Equal(name))
|
||||
}
|
||||
entities, err = kubeStore.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{
|
||||
SortBy: []datastore.SortOption{{Key: "model.createTime", Order: datastore.SortOrderDescending}},
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
FilterOptions: datastore.FilterOptions{
|
||||
Queries: []datastore.FuzzyQueryOption{{Key: "name", Query: "ir"}},
|
||||
},
|
||||
@@ -210,11 +216,6 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(count).Should(Equal(int64(4)))
|
||||
|
||||
app.Namespace = "test-namespace"
|
||||
count, err = kubeStore.Count(context.TODO(), &app, nil)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(count).Should(Equal(int64(1)))
|
||||
|
||||
count, err = kubeStore.Count(context.TODO(), &model.Cluster{}, &datastore.FilterOptions{
|
||||
Queries: []datastore.FuzzyQueryOption{{Key: "name", Query: "ir"}},
|
||||
})
|
||||
|
||||
@@ -219,7 +219,11 @@ func (m *mongodb) List(ctx context.Context, entity datastore.Entity, op *datasto
|
||||
if op != nil && len(op.SortBy) > 0 {
|
||||
_d := bson.D{}
|
||||
for _, sortOp := range op.SortBy {
|
||||
_d = append(_d, bson.E{Key: strings.ToLower(sortOp.Key), Value: int(sortOp.Order)})
|
||||
key := strings.ToLower(sortOp.Key)
|
||||
if key == "createtime" || key == "updatetime" {
|
||||
key = "basemodel." + key
|
||||
}
|
||||
_d = append(_d, bson.E{Key: key, Value: int(sortOp.Order)})
|
||||
}
|
||||
findOptions.SetSort(_d)
|
||||
}
|
||||
|
||||
@@ -63,14 +63,14 @@ var _ = Describe("Test mongodb datastore driver", func() {
|
||||
It("Test batch add function", func() {
|
||||
var datas = []datastore.Entity{
|
||||
&model.Application{Name: "kubevela-app-2", Description: "this is demo 2"},
|
||||
&model.Application{Namespace: "test-namespace", Name: "kubevela-app-3", Description: "this is demo 3"},
|
||||
&model.Application{Namespace: "test-namespace2", Name: "kubevela-app-4", Description: "this is demo 4"},
|
||||
&model.Application{Name: "kubevela-app-3", Description: "this is demo 3"},
|
||||
&model.Application{Name: "kubevela-app-4", Description: "this is demo 4"},
|
||||
}
|
||||
err := mongodbDriver.BatchAdd(context.TODO(), datas)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var datas2 = []datastore.Entity{
|
||||
&model.Application{Namespace: "test-namespace", Name: "can-delete", Description: "this is demo can-delete"},
|
||||
&model.Application{Name: "can-delete", Description: "this is demo can-delete"},
|
||||
&model.Application{Name: "kubevela-app-2", Description: "this is demo 2"},
|
||||
}
|
||||
err = mongodbDriver.BatchAdd(context.TODO(), datas2)
|
||||
@@ -111,12 +111,6 @@ var _ = Describe("Test mongodb datastore driver", func() {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 4)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
|
||||
app.Namespace = "test-namespace"
|
||||
list, err = mongodbDriver.List(context.TODO(), &app, nil)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 1)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test list clusters with sort and fuzzy query", func() {
|
||||
@@ -129,14 +123,25 @@ var _ = Describe("Test mongodb datastore driver", func() {
|
||||
Expect(mongodbDriver.Add(context.TODO(), &model.Cluster{Name: name})).Should(Succeed())
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
entities, err := mongodbDriver.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{SortBy: []datastore.SortOption{{Key: "model.createTime", Order: datastore.SortOrderAscending}}})
|
||||
entities, err := mongodbDriver.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderAscending}}})
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(len(entities)).Should(Equal(3))
|
||||
for i, name := range []string{"first", "second", "third"} {
|
||||
Expect(entities[i].(*model.Cluster).Name).Should(Equal(name))
|
||||
}
|
||||
entities, err = mongodbDriver.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{
|
||||
SortBy: []datastore.SortOption{{Key: "model.createTime", Order: datastore.SortOrderDescending}},
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
Page: 1,
|
||||
PageSize: 2,
|
||||
})
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(len(entities)).Should(Equal(2))
|
||||
for i, name := range []string{"third", "second"} {
|
||||
Expect(entities[i].(*model.Cluster).Name).Should(Equal(name))
|
||||
}
|
||||
entities, err = mongodbDriver.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
Page: 2,
|
||||
PageSize: 2,
|
||||
})
|
||||
@@ -146,7 +151,7 @@ var _ = Describe("Test mongodb datastore driver", func() {
|
||||
Expect(entities[i].(*model.Cluster).Name).Should(Equal(name))
|
||||
}
|
||||
entities, err = mongodbDriver.List(context.TODO(), &model.Cluster{}, &datastore.ListOptions{
|
||||
SortBy: []datastore.SortOption{{Key: "model.createTime", Order: datastore.SortOrderDescending}},
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
FilterOptions: datastore.FilterOptions{
|
||||
Queries: []datastore.FuzzyQueryOption{{Key: "name", Query: "ir"}},
|
||||
},
|
||||
@@ -164,11 +169,6 @@ var _ = Describe("Test mongodb datastore driver", func() {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(count).Should(Equal(int64(4)))
|
||||
|
||||
app.Namespace = "test-namespace"
|
||||
count, err = mongodbDriver.Count(context.TODO(), &app, nil)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(count).Should(Equal(int64(1)))
|
||||
|
||||
count, err = mongodbDriver.Count(context.TODO(), &model.Cluster{}, &datastore.FilterOptions{
|
||||
Queries: []datastore.FuzzyQueryOption{{Key: "name", Query: "ir"}},
|
||||
})
|
||||
|
||||
@@ -1,47 +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 model
|
||||
|
||||
import "github.com/oam-dev/kubevela/pkg/addon"
|
||||
|
||||
// AddonRegistry defines the data model of a AddonRegistry
|
||||
type AddonRegistry struct {
|
||||
Model
|
||||
Name string `json:"name"`
|
||||
|
||||
Git *addon.GitAddonSource `json:"git,omitempty"`
|
||||
Oss *addon.OSSAddonSource `json:"oss,omitempty"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
func (a *AddonRegistry) TableName() string {
|
||||
return tableNamePrefix + "addon_registry"
|
||||
}
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (a *AddonRegistry) PrimaryKey() string {
|
||||
return a.Name
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (a *AddonRegistry) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
if a.Name != "" {
|
||||
index["name"] = a.Name
|
||||
}
|
||||
return index
|
||||
}
|
||||
@@ -24,16 +24,15 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegistModel(&ApplicationComponent{}, &ApplicationPolicy{}, &Application{}, &ApplicationRevision{})
|
||||
RegistModel(&ApplicationComponent{}, &ApplicationPolicy{}, &Application{}, &ApplicationRevision{}, &ApplicationTrigger{})
|
||||
}
|
||||
|
||||
// Application application delivery model
|
||||
type Application struct {
|
||||
Model
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Project string `json:"project"`
|
||||
Namespace string `json:"namespace"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
@@ -55,9 +54,6 @@ func (a *Application) Index() map[string]string {
|
||||
if a.Name != "" {
|
||||
index["name"] = a.Name
|
||||
}
|
||||
if a.Namespace != "" {
|
||||
index["namespace"] = a.Namespace
|
||||
}
|
||||
if a.Project != "" {
|
||||
index["project"] = a.Project
|
||||
}
|
||||
@@ -78,7 +74,7 @@ type ComponentSelector struct {
|
||||
|
||||
// ApplicationComponent component database model
|
||||
type ApplicationComponent struct {
|
||||
Model
|
||||
BaseModel
|
||||
AppPrimaryKey string `json:"appPrimaryKey"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
@@ -128,7 +124,7 @@ func (a *ApplicationComponent) Index() map[string]string {
|
||||
|
||||
// ApplicationPolicy app policy
|
||||
type ApplicationPolicy struct {
|
||||
Model
|
||||
BaseModel
|
||||
AppPrimaryKey string `json:"appPrimaryKey"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
@@ -192,7 +188,7 @@ var RevisionStatusRollback = "rollback"
|
||||
|
||||
// ApplicationRevision be created when an application initiates deployment and describes the phased version of the application.
|
||||
type ApplicationRevision struct {
|
||||
Model
|
||||
BaseModel
|
||||
AppPrimaryKey string `json:"appPrimaryKey"`
|
||||
Version string `json:"version"`
|
||||
RollbackVersion string `json:"rollbackVersion,omitempty"`
|
||||
@@ -215,6 +211,18 @@ type ApplicationRevision struct {
|
||||
WorkflowName string `json:"workflowName"`
|
||||
// EnvName is the env name of this application revision
|
||||
EnvName string `json:"envName"`
|
||||
// CodeInfo is the code info of this application revision
|
||||
CodeInfo *CodeInfo `json:"codeInfo,omitempty"`
|
||||
}
|
||||
|
||||
// CodeInfo is the code info for webhook request
|
||||
type CodeInfo struct {
|
||||
// Commit is the commit hash
|
||||
Commit string `json:"commit,omitempty"`
|
||||
// Branch is the branch name
|
||||
Branch string `json:"branch,omitempty"`
|
||||
// User is the user name
|
||||
User string `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
@@ -253,3 +261,51 @@ func (a *ApplicationRevision) Index() map[string]string {
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// ApplicationTrigger is the model for trigger
|
||||
type ApplicationTrigger struct {
|
||||
BaseModel
|
||||
AppPrimaryKey string `json:"appPrimaryKey"`
|
||||
WorkflowName string `json:"workflowName,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Token string `json:"token"`
|
||||
Type string `json:"type"`
|
||||
PayloadType string `json:"payloadType"`
|
||||
}
|
||||
|
||||
const (
|
||||
// PayloadTypeCustom is the payload type custom
|
||||
PayloadTypeCustom = "custom"
|
||||
// PayloadTypeDockerhub is the payload type dockerhub
|
||||
PayloadTypeDockerhub = "dockerhub"
|
||||
)
|
||||
|
||||
// TableName return custom table name
|
||||
func (w *ApplicationTrigger) TableName() string {
|
||||
return tableNamePrefix + "trigger"
|
||||
}
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (w *ApplicationTrigger) PrimaryKey() string {
|
||||
return w.Token
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (w *ApplicationTrigger) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
if w.AppPrimaryKey != "" {
|
||||
index["appPrimaryKey"] = w.AppPrimaryKey
|
||||
}
|
||||
if w.Token != "" {
|
||||
index["token"] = w.Token
|
||||
}
|
||||
if w.Name != "" {
|
||||
index["name"] = w.Name
|
||||
}
|
||||
if w.Type != "" {
|
||||
index["type"] = w.Type
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ var (
|
||||
|
||||
// Cluster describes the model of cluster in apiserver
|
||||
type Cluster struct {
|
||||
Model `json:"model"`
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
|
||||
63
pkg/apiserver/model/env.go
Normal file
63
pkg/apiserver/model/env.go
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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 model
|
||||
|
||||
func init() {
|
||||
RegistModel(&Env{})
|
||||
}
|
||||
|
||||
// Env models the data of env in database
|
||||
type Env struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// Project defines the project this Env belongs to
|
||||
Project string `json:"project"`
|
||||
// Namespace defines the K8s namespace of the Env in control plane
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// Targets defines the name of delivery target that belongs to this env
|
||||
// In one project, a delivery target can only belong to one env.
|
||||
Targets []string `json:"targets,omitempty"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
func (p *Env) TableName() string {
|
||||
return tableNamePrefix + "env"
|
||||
}
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (p *Env) PrimaryKey() string {
|
||||
return p.Name
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (p *Env) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
if p.Namespace != "" {
|
||||
index["namespace"] = p.Namespace
|
||||
}
|
||||
if p.Project != "" {
|
||||
index["project"] = p.Project
|
||||
}
|
||||
return index
|
||||
}
|
||||
@@ -24,14 +24,25 @@ func init() {
|
||||
|
||||
// EnvBinding application env binding
|
||||
type EnvBinding struct {
|
||||
Model
|
||||
AppPrimaryKey string `json:"appPrimaryKey"`
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description,omitempty"`
|
||||
TargetNames []string `json:"targetNames"`
|
||||
ComponentSelector *ComponentSelector `json:"componentSelector"`
|
||||
//TODO: componentPatchs
|
||||
BaseModel
|
||||
AppPrimaryKey string `json:"appPrimaryKey"`
|
||||
Name string `json:"name"`
|
||||
ComponentsPatch []ComponentPatch `json:"componentsPatchs"`
|
||||
}
|
||||
|
||||
// ComponentPatch Define differential patches for components in the environment.
|
||||
type ComponentPatch struct {
|
||||
Name string `json:"name"`
|
||||
Properties *JSONStruct `json:"properties,omitempty"`
|
||||
Disable bool `json:"disable"`
|
||||
TraitsPatch []TraitPatch `json:"traitsPatch,omitempty"`
|
||||
}
|
||||
|
||||
// TraitPatch Define differential patches for traits in the environment.
|
||||
type TraitPatch struct {
|
||||
Type string `json:"type"`
|
||||
Properties *JSONStruct `json:"properties,omitempty"`
|
||||
Disable bool `json:"disable"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
|
||||
@@ -113,19 +113,19 @@ func (j *JSONStruct) RawExtension() *runtime.RawExtension {
|
||||
return &runtime.RawExtension{Raw: b}
|
||||
}
|
||||
|
||||
// Model common model
|
||||
type Model struct {
|
||||
// BaseModel common model
|
||||
type BaseModel struct {
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
|
||||
// SetCreateTime set create time
|
||||
func (m *Model) SetCreateTime(time time.Time) {
|
||||
func (m *BaseModel) SetCreateTime(time time.Time) {
|
||||
m.CreateTime = time
|
||||
}
|
||||
|
||||
// SetUpdateTime set update time
|
||||
func (m *Model) SetUpdateTime(time time.Time) {
|
||||
func (m *BaseModel) SetUpdateTime(time time.Time) {
|
||||
m.UpdateTime = time
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,10 @@ func init() {
|
||||
|
||||
// Project project model
|
||||
type Project struct {
|
||||
Model
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description,omitempty"`
|
||||
// Namespace Control cluster namespace
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
@@ -46,8 +44,5 @@ func (p *Project) Index() map[string]string {
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
if p.Namespace != "" {
|
||||
index["namespace"] = p.Namespace
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
@@ -17,16 +17,14 @@ limitations under the License.
|
||||
package model
|
||||
|
||||
func init() {
|
||||
RegistModel(&DeliveryTarget{})
|
||||
RegistModel(&Target{})
|
||||
}
|
||||
|
||||
// DeliveryTarget defines the delivery target information for the application
|
||||
// Target defines the delivery target information for the application
|
||||
// It includes kubernetes clusters or cloud service providers
|
||||
type DeliveryTarget struct {
|
||||
Model
|
||||
type Target struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Project string `json:"project"`
|
||||
Namespace string `json:"namespace"`
|
||||
Alias string `json:"alias,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Cluster *ClusterTarget `json:"cluster,omitempty"`
|
||||
@@ -34,31 +32,25 @@ type DeliveryTarget struct {
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
func (d *DeliveryTarget) TableName() string {
|
||||
return tableNamePrefix + "delivery_target"
|
||||
func (d *Target) TableName() string {
|
||||
return tableNamePrefix + "target"
|
||||
}
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (d *DeliveryTarget) PrimaryKey() string {
|
||||
func (d *Target) PrimaryKey() string {
|
||||
return d.Name
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (d *DeliveryTarget) Index() map[string]string {
|
||||
func (d *Target) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
if d.Name != "" {
|
||||
index["name"] = d.Name
|
||||
}
|
||||
if d.Namespace != "" {
|
||||
index["namespace"] = d.Namespace
|
||||
}
|
||||
if d.Project != "" {
|
||||
index["project"] = d.Project
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// ClusterTarget kubernetes delivery target
|
||||
// ClusterTarget one kubernetes cluster delivery target
|
||||
type ClusterTarget struct {
|
||||
ClusterName string `json:"clusterName" validate:"checkname"`
|
||||
Namespace string `json:"namespace" optional:"true"`
|
||||
@@ -31,7 +31,7 @@ func init() {
|
||||
|
||||
// Workflow application delivery database model
|
||||
type Workflow struct {
|
||||
Model
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
@@ -87,7 +87,7 @@ func (w *Workflow) Index() map[string]string {
|
||||
|
||||
// WorkflowRecord is the workflow record database model
|
||||
type WorkflowRecord struct {
|
||||
Model
|
||||
BaseModel
|
||||
WorkflowName string `json:"workflowName"`
|
||||
WorkflowAlias string `json:"workflowAlias"`
|
||||
AppPrimaryKey string `json:"appPrimaryKey"`
|
||||
|
||||
@@ -19,12 +19,11 @@ package v1
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/addon"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/cloudprovider"
|
||||
@@ -35,8 +34,8 @@ var (
|
||||
CtxKeyApplication = "application"
|
||||
// CtxKeyWorkflow request context key of workflow
|
||||
CtxKeyWorkflow = "workflow"
|
||||
// CtxKeyDeliveryTarget request context key of workflow
|
||||
CtxKeyDeliveryTarget = "delivery-target"
|
||||
// CtxKeyTarget request context key of workflow
|
||||
CtxKeyTarget = "delivery-target"
|
||||
// CtxKeyApplicationEnvBinding request context key of env binding
|
||||
CtxKeyApplicationEnvBinding = "envbinding-policy"
|
||||
// CtxKeyApplicationComponent request context key of component
|
||||
@@ -101,15 +100,27 @@ type EnableAddonRequest struct {
|
||||
|
||||
// ListAddonResponse defines the format for addon list response
|
||||
type ListAddonResponse struct {
|
||||
Addons []*addon.Meta `json:"addons"`
|
||||
Addons []*AddonInfo `json:"addons"`
|
||||
|
||||
// Message demonstrate the error info if exists
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// AddonInfo contain addon metaData and some baseInfo
|
||||
type AddonInfo struct {
|
||||
*addon.Meta
|
||||
RegistryName string `json:"registryName"`
|
||||
}
|
||||
|
||||
// ListEnabledAddonResponse defines the format for enabled addon list response
|
||||
type ListEnabledAddonResponse struct {
|
||||
EnabledAddons []*AddonStatusResponse
|
||||
EnabledAddons []*AddonBaseStatus `json:"enabledAddons"`
|
||||
}
|
||||
|
||||
// AddonBaseStatus addon base status
|
||||
type AddonBaseStatus struct {
|
||||
Name string `json:"name"`
|
||||
Phase AddonPhase `json:"phase"`
|
||||
}
|
||||
|
||||
// DetailAddonResponse defines the format for showing the addon details
|
||||
@@ -120,8 +131,9 @@ type DetailAddonResponse struct {
|
||||
UISchema []*utils.UIParameter `json:"uiSchema"`
|
||||
|
||||
// More details about the addon, e.g. README
|
||||
Detail string `json:"detail,omitempty"`
|
||||
Definitions []*AddonDefinition `json:"definitions"`
|
||||
Detail string `json:"detail,omitempty"`
|
||||
Definitions []*AddonDefinition `json:"definitions"`
|
||||
RegistryName string `json:"registryName,omitempty"`
|
||||
}
|
||||
|
||||
// AddonDefinition is definition an addon can provide
|
||||
@@ -134,12 +146,12 @@ type AddonDefinition struct {
|
||||
|
||||
// AddonStatusResponse defines the format of addon status response
|
||||
type AddonStatusResponse struct {
|
||||
Name string `json:"name"`
|
||||
Phase AddonPhase `json:"phase"`
|
||||
Args map[string]string `json:"args"`
|
||||
|
||||
AddonBaseStatus
|
||||
Args map[string]string `json:"args"`
|
||||
EnablingProgress *EnablingProgress `json:"enabling_progress,omitempty"`
|
||||
AppStatus common.AppStatus `json:"appStatus,omitempty"`
|
||||
// the status of multiple clusters
|
||||
Clusters map[string]map[string]interface{} `json:"clusters,omitempty"`
|
||||
}
|
||||
|
||||
// EnablingProgress defines the progress of enabling an addon
|
||||
@@ -265,9 +277,10 @@ type ClusterBase struct {
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// ListApplicatioOptions list application query options
|
||||
type ListApplicatioOptions struct {
|
||||
// ListApplicationOptions list application query options
|
||||
type ListApplicationOptions struct {
|
||||
Project string `json:"project"`
|
||||
Env string `json:"env"`
|
||||
TargetName string `json:"targetName"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
@@ -280,16 +293,6 @@ type ListApplicationResponse struct {
|
||||
// EnvBindingList env binding list
|
||||
type EnvBindingList []*EnvBinding
|
||||
|
||||
// ContainTarget contain cluster name
|
||||
func (e EnvBindingList) ContainTarget(name string) bool {
|
||||
for _, eb := range e {
|
||||
if utils.StringsContain(eb.TargetNames, name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ApplicationBase application base model
|
||||
type ApplicationBase struct {
|
||||
Name string `json:"name"`
|
||||
@@ -310,13 +313,13 @@ type ApplicationStatusResponse struct {
|
||||
|
||||
// ApplicationStatisticsResponse application statistics response body
|
||||
type ApplicationStatisticsResponse struct {
|
||||
EnvCount int64 `json:"envCount"`
|
||||
DeliveryTargetCount int64 `json:"deliveryTargetCount"`
|
||||
RevisonCount int64 `json:"revisonCount"`
|
||||
WorkflowCount int64 `json:"workflowCount"`
|
||||
EnvCount int64 `json:"envCount"`
|
||||
TargetCount int64 `json:"targetCount"`
|
||||
RevisonCount int64 `json:"revisonCount"`
|
||||
WorkflowCount int64 `json:"workflowCount"`
|
||||
}
|
||||
|
||||
// CreateApplicationRequest create application request body
|
||||
// CreateApplicationRequest create application request body
|
||||
type CreateApplicationRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
@@ -325,11 +328,10 @@ type CreateApplicationRequest struct {
|
||||
Icon string `json:"icon"`
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
EnvBinding []*EnvBinding `json:"envBinding,omitempty"`
|
||||
YamlConfig string `json:"yamlConfig,omitempty"`
|
||||
Component *CreateComponentRequest `json:"component"`
|
||||
}
|
||||
|
||||
// UpdateApplicationRequest update application base config
|
||||
// UpdateApplicationRequest update application base config
|
||||
type UpdateApplicationRequest struct {
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description" optional:"true"`
|
||||
@@ -337,13 +339,44 @@ type UpdateApplicationRequest struct {
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// CreateApplicationTriggerRequest create application trigger
|
||||
type CreateApplicationTriggerRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description" optional:"true"`
|
||||
WorkflowName string `json:"workflowName"`
|
||||
Type string `json:"type" validate:"oneof=webhook"`
|
||||
PayloadType string `json:"payloadType" validate:"oneof=custom"`
|
||||
}
|
||||
|
||||
// ApplicationTriggerBase application trigger base model
|
||||
type ApplicationTriggerBase struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
WorkflowName string `json:"workflowName"`
|
||||
Type string `json:"type"`
|
||||
PayloadType string `json:"payloadType"`
|
||||
Token string `json:"token"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
|
||||
// ListApplicationTriggerResponse list application triggers response body
|
||||
type ListApplicationTriggerResponse struct {
|
||||
Triggers []*ApplicationTriggerBase `json:"triggers"`
|
||||
}
|
||||
|
||||
// HandleApplicationWebhookRequest handles application webhook request
|
||||
type HandleApplicationWebhookRequest struct {
|
||||
Upgrade map[string]*model.JSONStruct `json:"upgrade,omitempty"`
|
||||
CodeInfo *model.CodeInfo `json:"codeInfo,omitempty"`
|
||||
}
|
||||
|
||||
// EnvBinding application env binding
|
||||
type EnvBinding struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
TargetNames []string `json:"targetNames"`
|
||||
ComponentSelector *ComponentSelector `json:"componentSelector" optional:"true"`
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
//TODO: support componentsPatch
|
||||
}
|
||||
|
||||
// EnvBindingTarget the target struct in the envbinding base struct
|
||||
@@ -354,15 +387,16 @@ type EnvBindingTarget struct {
|
||||
|
||||
// EnvBindingBase application env binding
|
||||
type EnvBindingBase struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
TargetNames []string `json:"targetNames"`
|
||||
Targets []EnvBindingTarget `json:"deliveryTargets,omitempty"`
|
||||
ComponentSelector *ComponentSelector `json:"componentSelector" optional:"true"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
AppDeployName string `json:"appDeployName"`
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
TargetNames []string `json:"targetNames"`
|
||||
Targets []EnvBindingTarget `json:"targets,omitempty"`
|
||||
ComponentSelector *ComponentSelector `json:"componentSelector" optional:"true"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
AppDeployName string `json:"appDeployName"`
|
||||
AppDeployNamespace string `json:"appDeployNamespace"`
|
||||
}
|
||||
|
||||
// DetailEnvBindingResponse defines the response of env-binding details
|
||||
@@ -486,7 +520,6 @@ type ProjectBase struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description"`
|
||||
Namespace string `json:"namespace"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
@@ -496,7 +529,60 @@ type CreateProjectRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description" optional:"true"`
|
||||
Namespace string `json:"namespace" optional:"true"`
|
||||
}
|
||||
|
||||
// Env models the data of env in API
|
||||
type Env struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
|
||||
// Project defines the project this Env belongs to
|
||||
Project NameAlias `json:"project"`
|
||||
// Namespace defines the K8s namespace of the Env in control plane
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// Targets defines the name of delivery target that belongs to this env
|
||||
// In one project, a delivery target can only belong to one env.
|
||||
Targets []NameAlias `json:"targets,omitempty" optional:"true"`
|
||||
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
|
||||
// ListEnvOptions list envs by query options
|
||||
type ListEnvOptions struct {
|
||||
Project string `json:"project"`
|
||||
}
|
||||
|
||||
// ListEnvResponse response the while env list
|
||||
type ListEnvResponse struct {
|
||||
Envs []*Env `json:"envs"`
|
||||
}
|
||||
|
||||
// CreateEnvRequest contains the env data as request body
|
||||
type CreateEnvRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
|
||||
// Project defines the project this Env belongs to
|
||||
Project string `json:"project"`
|
||||
// Namespace defines the K8s namespace of the Env in control plane
|
||||
Namespace string `json:"namespace"`
|
||||
|
||||
// Targets defines the name of delivery target that belongs to this env
|
||||
// In one project, a delivery target can only belong to one env.
|
||||
Targets []string `json:"targets,omitempty" optional:"true"`
|
||||
}
|
||||
|
||||
// UpdateEnvRequest defines the data of Env for update
|
||||
type UpdateEnvRequest struct {
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
// Targets defines the name of delivery target that belongs to this env
|
||||
// In one project, a delivery target can only belong to one env.
|
||||
Targets []string `json:"targets,omitempty" optional:"true"`
|
||||
}
|
||||
|
||||
// ListDefinitionResponse list definition response model
|
||||
@@ -589,8 +675,7 @@ type UpdateWorkflowRequest struct {
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description" optional:"true"`
|
||||
Steps []WorkflowStep `json:"steps,omitempty"`
|
||||
Enable bool `json:"enable"`
|
||||
Default bool `json:"default"`
|
||||
Default *bool `json:"default"`
|
||||
}
|
||||
|
||||
// WorkflowStep workflow step config
|
||||
@@ -635,13 +720,22 @@ type ListWorkflowRecordsResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
const (
|
||||
// TriggerTypeWeb means trigger by web
|
||||
TriggerTypeWeb string = "web"
|
||||
// TriggerTypeAPI means trigger by api
|
||||
TriggerTypeAPI string = "api"
|
||||
// TriggerTypeWebhook means trigger by webhook
|
||||
TriggerTypeWebhook string = "webhook"
|
||||
)
|
||||
|
||||
// DetailWorkflowRecordResponse get workflow record detail
|
||||
type DetailWorkflowRecordResponse struct {
|
||||
WorkflowRecord
|
||||
DeployTime time.Time `json:"deployTime"`
|
||||
DeployUser string `json:"deployUser"`
|
||||
Note string `json:"note"`
|
||||
// TriggerType the event trigger source, Web or API
|
||||
// TriggerType the event trigger source, Web or API or Webhook
|
||||
TriggerType string `json:"triggerType"`
|
||||
}
|
||||
|
||||
@@ -662,10 +756,12 @@ type ApplicationDeployRequest struct {
|
||||
WorkflowName string `json:"workflowName"`
|
||||
// User note message, optional
|
||||
Note string `json:"note"`
|
||||
// TriggerType the event trigger source, Web or API
|
||||
TriggerType string `json:"triggerType" validate:"oneof=web api"`
|
||||
// TriggerType the event trigger source, Web or API or Webhook
|
||||
TriggerType string `json:"triggerType" validate:"oneof=web api webhook"`
|
||||
// Force set to True to ignore unfinished events.
|
||||
Force bool `json:"force"`
|
||||
// CodeInfo is the source code info of this deploy
|
||||
CodeInfo *model.CodeInfo `json:"gitInfo,omitempty"`
|
||||
}
|
||||
|
||||
// ApplicationDeployResponse application deploy response body
|
||||
@@ -676,12 +772,8 @@ type ApplicationDeployResponse struct {
|
||||
// VelaQLViewResponse query response
|
||||
type VelaQLViewResponse map[string]interface{}
|
||||
|
||||
// PutApplicationEnvRequest set diff request
|
||||
type PutApplicationEnvRequest struct {
|
||||
ComponentSelector *ComponentSelector `json:"componentSelector,omitempty"`
|
||||
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
TargetNames []string `json:"targetNames"`
|
||||
// PutApplicationEnvBindingRequest update app envbinding request body
|
||||
type PutApplicationEnvBindingRequest struct {
|
||||
}
|
||||
|
||||
// ListApplicationEnvBinding list app envBindings
|
||||
@@ -689,8 +781,8 @@ type ListApplicationEnvBinding struct {
|
||||
EnvBindings []*EnvBindingBase `json:"envBindings"`
|
||||
}
|
||||
|
||||
// CreateApplicationEnvRequest new application env
|
||||
type CreateApplicationEnvRequest struct {
|
||||
// CreateApplicationEnvbindingRequest new application env
|
||||
type CreateApplicationEnvbindingRequest struct {
|
||||
EnvBinding
|
||||
}
|
||||
|
||||
@@ -721,21 +813,19 @@ type ApplicationTrait struct {
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
|
||||
// CreateDeliveryTargetRequest create delivery target request body
|
||||
type CreateDeliveryTargetRequest struct {
|
||||
// CreateTargetRequest create delivery target request body
|
||||
type CreateTargetRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Project string `json:"project" validate:"checkname"`
|
||||
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
Cluster *ClusterTarget `json:"cluster,omitempty"`
|
||||
Variable map[string]interface{} `json:"variable,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateDeliveryTargetRequest only support full quantity update
|
||||
type UpdateDeliveryTargetRequest struct {
|
||||
// UpdateTargetRequest only support full quantity update
|
||||
type UpdateTargetRequest struct {
|
||||
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
Cluster *ClusterTarget `json:"cluster,omitempty"`
|
||||
Variable map[string]interface{} `json:"variable,omitempty"`
|
||||
}
|
||||
|
||||
@@ -745,21 +835,20 @@ type ClusterTarget struct {
|
||||
Namespace string `json:"namespace" optional:"true"`
|
||||
}
|
||||
|
||||
// DetailDeliveryTargetResponse detail deliveryTarget response
|
||||
type DetailDeliveryTargetResponse struct {
|
||||
DeliveryTargetBase
|
||||
// DetailTargetResponse detail Target response
|
||||
type DetailTargetResponse struct {
|
||||
TargetBase
|
||||
}
|
||||
|
||||
// ListTargetResponse list delivery target response body
|
||||
type ListTargetResponse struct {
|
||||
Targets []DeliveryTargetBase `json:"targets"`
|
||||
Total int64 `json:"total"`
|
||||
Targets []TargetBase `json:"targets"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// DeliveryTargetBase deliveryTarget base model
|
||||
type DeliveryTargetBase struct {
|
||||
// TargetBase Target base model
|
||||
type TargetBase struct {
|
||||
Name string `json:"name"`
|
||||
Project *ProjectBase `json:"project"`
|
||||
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
Cluster *ClusterTarget `json:"cluster,omitempty"`
|
||||
@@ -775,12 +864,14 @@ type ApplicationRevisionBase struct {
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
Version string `json:"version"`
|
||||
Status string `json:"status"`
|
||||
Reason string `json:"reason"`
|
||||
DeployUser string `json:"deployUser"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
DeployUser string `json:"deployUser,omitempty"`
|
||||
Note string `json:"note"`
|
||||
EnvName string `json:"envName"`
|
||||
// SourceType the event trigger source, Web or API
|
||||
// SourceType the event trigger source, Web or API or Webhook
|
||||
TriggerType string `json:"triggerType"`
|
||||
// CodeInfo is the code info of this application revision
|
||||
CodeInfo *model.CodeInfo `json:"codeInfo,omitempty"`
|
||||
}
|
||||
|
||||
// ListRevisionsResponse list application revisions
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore/mongodb"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/webservice"
|
||||
)
|
||||
|
||||
@@ -154,7 +155,7 @@ func (s *restServer) setupLeaderElection() (*leaderelection.LeaderElectionConfig
|
||||
}
|
||||
|
||||
func (s restServer) runLeader(ctx context.Context, duration time.Duration) {
|
||||
w := usecase.NewWorkflowUsecase(s.dataStore)
|
||||
w := usecase.NewWorkflowUsecase(s.dataStore, usecase.NewEnvUsecase(s.dataStore))
|
||||
|
||||
t := time.NewTicker(duration)
|
||||
defer t.Stop()
|
||||
@@ -190,8 +191,11 @@ func (s *restServer) RegisterServices() restfulspec.Config {
|
||||
// Add container filter to respond to OPTIONS
|
||||
s.webContainer.Filter(s.webContainer.OPTIONSFilter)
|
||||
|
||||
// Add request log
|
||||
s.webContainer.Filter(s.requestLog)
|
||||
|
||||
// Regist all custom webservice
|
||||
for _, handler := range webservice.GetRegistedWebService() {
|
||||
for _, handler := range webservice.GetRegisteredWebService() {
|
||||
s.webContainer.Add(handler.GetWebService())
|
||||
}
|
||||
|
||||
@@ -203,6 +207,22 @@ func (s *restServer) RegisterServices() restfulspec.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (s *restServer) requestLog(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
|
||||
start := time.Now()
|
||||
c := utils.NewResponseCapture(resp.ResponseWriter)
|
||||
resp.ResponseWriter = c
|
||||
chain.ProcessFilter(req, resp)
|
||||
takeTime := time.Since(start)
|
||||
log.Logger.With(
|
||||
"clientIP", utils.ClientIP(req.Request),
|
||||
"path", req.Request.URL.Path,
|
||||
"method", req.Request.Method,
|
||||
"status", c.StatusCode(),
|
||||
"time", takeTime.String(),
|
||||
"responseSize", len(c.Bytes()),
|
||||
).Infof("request log")
|
||||
}
|
||||
|
||||
func enrichSwaggerObject(swo *spec.Swagger) {
|
||||
swo.Info = &spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
|
||||
@@ -39,10 +39,10 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
restutils "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
velaerr "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
)
|
||||
|
||||
// AddonHandler handle CRUD and installation of addons
|
||||
@@ -57,7 +57,7 @@ type AddonHandler interface {
|
||||
GetAddon(ctx context.Context, name string, registry string) (*apis.DetailAddonResponse, error)
|
||||
EnableAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error
|
||||
DisableAddon(ctx context.Context, name string) error
|
||||
ListEnabledAddon(ctx context.Context) ([]*apis.AddonStatusResponse, error)
|
||||
ListEnabledAddon(ctx context.Context) ([]*apis.AddonBaseStatus, error)
|
||||
UpdateAddon(ctx context.Context, name string, args apis.EnableAddonRequest) error
|
||||
}
|
||||
|
||||
@@ -78,11 +78,12 @@ func AddonImpl2AddonRes(impl *pkgaddon.UIData) (*apis.DetailAddonResponse, error
|
||||
})
|
||||
}
|
||||
return &apis.DetailAddonResponse{
|
||||
Meta: impl.Meta,
|
||||
APISchema: impl.APISchema,
|
||||
UISchema: impl.UISchema,
|
||||
Detail: impl.Detail,
|
||||
Definitions: defs,
|
||||
Meta: impl.Meta,
|
||||
APISchema: impl.APISchema,
|
||||
UISchema: impl.UISchema,
|
||||
Detail: impl.Detail,
|
||||
Definitions: defs,
|
||||
RegistryName: impl.RegistryName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -163,7 +164,6 @@ func (u *defaultAddonHandler) GetAddon(ctx context.Context, name string, registr
|
||||
}
|
||||
|
||||
func (u *defaultAddonHandler) StatusAddon(ctx context.Context, name string) (*apis.AddonStatusResponse, error) {
|
||||
|
||||
status, err := pkgaddon.GetAddonStatus(ctx, u.kubeClient, name)
|
||||
if err != nil {
|
||||
return nil, bcode.ErrGetAddonApplication
|
||||
@@ -171,14 +171,20 @@ func (u *defaultAddonHandler) StatusAddon(ctx context.Context, name string) (*ap
|
||||
|
||||
if status.AddonPhase == string(apis.AddonPhaseDisabled) {
|
||||
return &apis.AddonStatusResponse{
|
||||
Phase: apis.AddonPhase(status.AddonPhase),
|
||||
AddonBaseStatus: apis.AddonBaseStatus{
|
||||
Name: name,
|
||||
Phase: apis.AddonPhase(status.AddonPhase),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
res := apis.AddonStatusResponse{
|
||||
Name: name,
|
||||
Phase: apis.AddonPhase(status.AddonPhase),
|
||||
AddonBaseStatus: apis.AddonBaseStatus{
|
||||
Name: name,
|
||||
Phase: apis.AddonPhase(status.AddonPhase),
|
||||
},
|
||||
AppStatus: *status.AppStatus,
|
||||
Clusters: status.Clusters,
|
||||
}
|
||||
|
||||
if res.Phase != apis.AddonPhaseEnabled {
|
||||
@@ -196,8 +202,8 @@ func (u *defaultAddonHandler) StatusAddon(ctx context.Context, name string) (*ap
|
||||
for k, v := range sec.Data {
|
||||
res.Args[k] = string(v)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
@@ -208,7 +214,7 @@ func (u *defaultAddonHandler) ListAddons(ctx context.Context, registry, query st
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var gatherErr restutils.GatherErr
|
||||
var gatherErr velaerr.ErrorList
|
||||
|
||||
for _, r := range rs {
|
||||
if registry != "" && r.Name != registry {
|
||||
@@ -255,7 +261,10 @@ func (u *defaultAddonHandler) ListAddons(ctx context.Context, registry, query st
|
||||
}
|
||||
addonResources = append(addonResources, addonRes)
|
||||
}
|
||||
return addonResources, gatherErr
|
||||
if gatherErr.HasError() {
|
||||
return addonResources, gatherErr
|
||||
}
|
||||
return addonResources, nil
|
||||
}
|
||||
|
||||
func (u *defaultAddonHandler) DeleteAddonRegistry(ctx context.Context, name string) error {
|
||||
@@ -358,18 +367,18 @@ func (u *defaultAddonHandler) DisableAddon(ctx context.Context, name string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *defaultAddonHandler) ListEnabledAddon(ctx context.Context) ([]*apis.AddonStatusResponse, error) {
|
||||
func (u *defaultAddonHandler) ListEnabledAddon(ctx context.Context) ([]*apis.AddonBaseStatus, error) {
|
||||
apps := &v1beta1.ApplicationList{}
|
||||
if err := u.kubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS), client.HasLabels{oam.LabelAddonName}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response []*apis.AddonStatusResponse
|
||||
var response []*apis.AddonBaseStatus
|
||||
for _, application := range apps.Items {
|
||||
if addonName := application.Labels[oam.LabelAddonName]; addonName != "" {
|
||||
if application.Status.Phase != common2.ApplicationRunning {
|
||||
continue
|
||||
}
|
||||
response = append(response, &apis.AddonStatusResponse{
|
||||
response = append(response, &apis.AddonBaseStatus{
|
||||
Name: addonName,
|
||||
Phase: convertAppStateToAddonPhase(application.Status.Phase),
|
||||
})
|
||||
@@ -426,6 +435,9 @@ func mergeAddons(a1, a2 []*pkgaddon.UIData) []*pkgaddon.UIData {
|
||||
}
|
||||
|
||||
func hasAddon(addons []*pkgaddon.UIData, name string) bool {
|
||||
if name == "" {
|
||||
return true
|
||||
}
|
||||
for _, addon := range addons {
|
||||
if addon.Name == name {
|
||||
return true
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -57,11 +58,13 @@ const (
|
||||
|
||||
// EnvBindingPolicyDefaultName default policy name
|
||||
EnvBindingPolicyDefaultName string = "env-bindings"
|
||||
|
||||
defaultTokenLen int = 16
|
||||
)
|
||||
|
||||
// ApplicationUsecase application usecase
|
||||
type ApplicationUsecase interface {
|
||||
ListApplications(ctx context.Context, listOptions apisv1.ListApplicatioOptions) ([]*apisv1.ApplicationBase, error)
|
||||
ListApplications(ctx context.Context, listOptions apisv1.ListApplicationOptions) ([]*apisv1.ApplicationBase, error)
|
||||
GetApplication(ctx context.Context, appName string) (*model.Application, error)
|
||||
GetApplicationStatus(ctx context.Context, app *model.Application, envName string) (*common.AppStatus, error)
|
||||
DetailApplication(ctx context.Context, app *model.Application) (*apisv1.DetailApplicationResponse, error)
|
||||
@@ -88,24 +91,28 @@ type ApplicationUsecase interface {
|
||||
DetailRevision(ctx context.Context, appName, revisionName string) (*apisv1.DetailRevisionResponse, error)
|
||||
Statistics(ctx context.Context, app *model.Application) (*apisv1.ApplicationStatisticsResponse, error)
|
||||
ListRecords(ctx context.Context, appName string) (*apisv1.ListWorkflowRecordsResponse, error)
|
||||
CreateApplicationTrigger(ctx context.Context, app *model.Application, req apisv1.CreateApplicationTriggerRequest) (*apisv1.ApplicationTriggerBase, error)
|
||||
ListApplicationTriggers(ctx context.Context, app *model.Application) ([]*apisv1.ApplicationTriggerBase, error)
|
||||
}
|
||||
|
||||
type applicationUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
kubeClient client.Client
|
||||
apply apply.Applicator
|
||||
workflowUsecase WorkflowUsecase
|
||||
envBindingUsecase EnvBindingUsecase
|
||||
deliveryTargetUsecase DeliveryTargetUsecase
|
||||
definitionUsecase DefinitionUsecase
|
||||
projectUsecase ProjectUsecase
|
||||
ds datastore.DataStore
|
||||
kubeClient client.Client
|
||||
apply apply.Applicator
|
||||
workflowUsecase WorkflowUsecase
|
||||
envUsecase EnvUsecase
|
||||
envBindingUsecase EnvBindingUsecase
|
||||
targetUsecase TargetUsecase
|
||||
definitionUsecase DefinitionUsecase
|
||||
projectUsecase ProjectUsecase
|
||||
}
|
||||
|
||||
// NewApplicationUsecase new application usecase
|
||||
func NewApplicationUsecase(ds datastore.DataStore,
|
||||
workflowUsecase WorkflowUsecase,
|
||||
envBindingUsecase EnvBindingUsecase,
|
||||
deliveryTargetUsecase DeliveryTargetUsecase,
|
||||
envUsecase EnvUsecase,
|
||||
targetUsecase TargetUsecase,
|
||||
definitionUsecase DefinitionUsecase,
|
||||
projectUsecase ProjectUsecase,
|
||||
) ApplicationUsecase {
|
||||
@@ -114,43 +121,82 @@ func NewApplicationUsecase(ds datastore.DataStore,
|
||||
log.Logger.Fatalf("get kubeclient failure %s", err.Error())
|
||||
}
|
||||
return &applicationUsecaseImpl{
|
||||
ds: ds,
|
||||
workflowUsecase: workflowUsecase,
|
||||
envBindingUsecase: envBindingUsecase,
|
||||
deliveryTargetUsecase: deliveryTargetUsecase,
|
||||
kubeClient: kubecli,
|
||||
apply: apply.NewAPIApplicator(kubecli),
|
||||
definitionUsecase: definitionUsecase,
|
||||
projectUsecase: projectUsecase,
|
||||
ds: ds,
|
||||
workflowUsecase: workflowUsecase,
|
||||
envBindingUsecase: envBindingUsecase,
|
||||
targetUsecase: targetUsecase,
|
||||
kubeClient: kubecli,
|
||||
apply: apply.NewAPIApplicator(kubecli),
|
||||
definitionUsecase: definitionUsecase,
|
||||
projectUsecase: projectUsecase,
|
||||
envUsecase: envUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
// ListApplications list applications
|
||||
func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptions apisv1.ListApplicatioOptions) ([]*apisv1.ApplicationBase, error) {
|
||||
func listApp(ctx context.Context, ds datastore.DataStore, listOptions apisv1.ListApplicationOptions) ([]*model.Application, error) {
|
||||
var app = model.Application{}
|
||||
if listOptions.Project != "" {
|
||||
app.Project = listOptions.Project
|
||||
}
|
||||
entitys, err := c.ds.List(ctx, &app, &datastore.ListOptions{})
|
||||
var err error
|
||||
var envBinding []*apisv1.EnvBindingBase
|
||||
if listOptions.Env != "" || listOptions.TargetName != "" {
|
||||
envBinding, err = listFullEnvBinding(ctx, ds, envListOption{})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list envbinding for list application in env %s err %v", listOptions.Env, err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
entities, err := ds.List(ctx, &app, &datastore.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []*apisv1.ApplicationBase
|
||||
for _, entity := range entitys {
|
||||
appModel := entity.(*model.Application)
|
||||
appBase := c.converAppModelToBase(ctx, appModel)
|
||||
var list []*model.Application
|
||||
for _, entity := range entities {
|
||||
appModel, ok := entity.(*model.Application)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if listOptions.Query != "" &&
|
||||
!(strings.Contains(appBase.Alias, listOptions.Query) ||
|
||||
strings.Contains(appBase.Name, listOptions.Query) ||
|
||||
strings.Contains(appBase.Description, listOptions.Query)) {
|
||||
!(strings.Contains(appModel.Alias, listOptions.Query) ||
|
||||
strings.Contains(appModel.Name, listOptions.Query) ||
|
||||
strings.Contains(appModel.Description, listOptions.Query)) {
|
||||
continue
|
||||
}
|
||||
if listOptions.TargetName != "" {
|
||||
targetIsContain, _ := c.envBindingUsecase.CheckAppEnvBindingsContainTarget(ctx, appModel, listOptions.TargetName)
|
||||
targetIsContain, _ := CheckAppEnvBindingsContainTarget(envBinding, listOptions.TargetName)
|
||||
if !targetIsContain {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(envBinding) > 0 && listOptions.Env != "" {
|
||||
check := func() bool {
|
||||
for _, eb := range envBinding {
|
||||
if eb.Name == listOptions.Env && appModel.PrimaryKey() == eb.AppDeployName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if !check() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
list = append(list, appModel)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// ListApplications list applications
|
||||
func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptions apisv1.ListApplicationOptions) ([]*apisv1.ApplicationBase, error) {
|
||||
apps, err := listApp(ctx, c.ds, listOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []*apisv1.ApplicationBase
|
||||
for _, app := range apps {
|
||||
appBase := c.converAppModelToBase(ctx, app)
|
||||
list = append(list, appBase)
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
@@ -176,7 +222,7 @@ func (c *applicationUsecaseImpl) GetApplication(ctx context.Context, appName str
|
||||
// DetailApplication detail application info
|
||||
func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *model.Application) (*apisv1.DetailApplicationResponse, error) {
|
||||
base := c.converAppModelToBase(ctx, app)
|
||||
policys, err := c.queryApplicationPolicys(ctx, app)
|
||||
policys, err := c.queryApplicationPolicies(ctx, app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -205,7 +251,7 @@ func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *mod
|
||||
ComponentNum: componentNum,
|
||||
},
|
||||
ApplicationType: func() string {
|
||||
if c.envBindingUsecase.GetSuitableType(ctx, app) == DeployCloudResource {
|
||||
if GetSuitableDeployWay(ctx, c.kubeClient, c.ds, app) == DeployCloudResource {
|
||||
return "cloud"
|
||||
}
|
||||
return "common"
|
||||
@@ -217,7 +263,11 @@ func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *mod
|
||||
// GetApplicationStatus get application status from controller cluster
|
||||
func (c *applicationUsecaseImpl) GetApplicationStatus(ctx context.Context, appmodel *model.Application, envName string) (*common.AppStatus, error) {
|
||||
var app v1beta1.Application
|
||||
err := c.kubeClient.Get(ctx, types.NamespacedName{Namespace: appmodel.Namespace, Name: convertAppName(appmodel.Name, envName)}, &app)
|
||||
env, err := c.envUsecase.GetEnv(ctx, envName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.kubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appmodel.Name}, &app)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
@@ -230,7 +280,7 @@ func (c *applicationUsecaseImpl) GetApplicationStatus(ctx context.Context, appmo
|
||||
return &app.Status, nil
|
||||
}
|
||||
|
||||
// GetApplicationCR get application cr in cluster
|
||||
// GetApplicationCR get application CR in cluster
|
||||
func (c *applicationUsecaseImpl) GetApplicationCR(ctx context.Context, appModel *model.Application) (*v1beta1.ApplicationList, error) {
|
||||
var apps v1beta1.ApplicationList
|
||||
selector := labels.NewSelector()
|
||||
@@ -241,7 +291,6 @@ func (c *applicationUsecaseImpl) GetApplicationCR(ctx context.Context, appModel
|
||||
selector = selector.Add(*re)
|
||||
err = c.kubeClient.List(ctx, &apps, &client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
Namespace: appModel.Namespace,
|
||||
})
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
@@ -282,29 +331,8 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
application.Namespace = project.Namespace
|
||||
application.Project = project.Name
|
||||
|
||||
if req.YamlConfig != "" {
|
||||
var oamApp v1beta1.Application
|
||||
if err := yaml.Unmarshal([]byte(req.YamlConfig), &oamApp); err != nil {
|
||||
log.Logger.Errorf("application yaml config is invalid,%s", err.Error())
|
||||
return nil, bcode.ErrApplicationConfig
|
||||
}
|
||||
|
||||
// split the configuration and store it in the database.
|
||||
if err := c.saveApplicationComponent(ctx, &application, oamApp.Spec.Components); err != nil {
|
||||
log.Logger.Errorf("save applictaion component failure,%s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if len(oamApp.Spec.Policies) > 0 {
|
||||
if err := c.saveApplicationPolicy(ctx, &application, oamApp.Spec.Policies); err != nil {
|
||||
log.Logger.Errorf("save applictaion polocies failure,%s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if req.Component != nil {
|
||||
_, err = c.AddComponent(ctx, &application, *req.Component)
|
||||
if err != nil {
|
||||
@@ -318,6 +346,14 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := c.CreateApplicationTrigger(ctx, &application, apisv1.CreateApplicationTriggerRequest{
|
||||
Name: fmt.Sprintf("%s-%s", application.Name, "default"),
|
||||
PayloadType: model.PayloadTypeCustom,
|
||||
Type: apisv1.TriggerTypeWebhook,
|
||||
WorkflowName: convertWorkflowName(req.EnvBinding[0].Name),
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// add application to db.
|
||||
if err := c.ds.Add(ctx, &application); err != nil {
|
||||
@@ -331,6 +367,67 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// CreateApplicationTrigger create application trigger
|
||||
func (c *applicationUsecaseImpl) CreateApplicationTrigger(ctx context.Context, app *model.Application, req apisv1.CreateApplicationTriggerRequest) (*apisv1.ApplicationTriggerBase, error) {
|
||||
trigger := &model.ApplicationTrigger{
|
||||
AppPrimaryKey: app.Name,
|
||||
WorkflowName: req.WorkflowName,
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
Type: req.Type,
|
||||
PayloadType: req.PayloadType,
|
||||
Token: genWebhookToken(),
|
||||
}
|
||||
if err := c.ds.Add(ctx, trigger); err != nil {
|
||||
log.Logger.Errorf("failed to create application trigger, %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &apisv1.ApplicationTriggerBase{
|
||||
WorkflowName: req.WorkflowName,
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
Type: req.Type,
|
||||
PayloadType: req.PayloadType,
|
||||
Token: trigger.Token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ListApplicationTrigger list application triggers
|
||||
func (c *applicationUsecaseImpl) ListApplicationTriggers(ctx context.Context, app *model.Application) ([]*apisv1.ApplicationTriggerBase, error) {
|
||||
trigger := &model.ApplicationTrigger{
|
||||
AppPrimaryKey: app.Name,
|
||||
}
|
||||
triggers, err := c.ds.List(ctx, trigger, &datastore.ListOptions{
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}},
|
||||
)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("failed to list application triggers, %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := []*apisv1.ApplicationTriggerBase{}
|
||||
for _, raw := range triggers {
|
||||
trigger, ok := raw.(*model.ApplicationTrigger)
|
||||
if ok {
|
||||
resp = append(resp, &apisv1.ApplicationTriggerBase{
|
||||
WorkflowName: trigger.WorkflowName,
|
||||
Name: trigger.Name,
|
||||
Alias: trigger.Alias,
|
||||
Description: trigger.Description,
|
||||
Type: trigger.Type,
|
||||
PayloadType: trigger.PayloadType,
|
||||
Token: trigger.Token,
|
||||
UpdateTime: trigger.UpdateTime,
|
||||
CreateTime: trigger.CreateTime,
|
||||
})
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *applicationUsecaseImpl) genPolicyByEnv(ctx context.Context, app *model.Application, envName string, components []*model.ApplicationComponent) (v1beta1.AppPolicy, error) {
|
||||
appPolicy := v1beta1.AppPolicy{}
|
||||
envBinding, err := c.envBindingUsecase.GetEnvBinding(ctx, app, envName)
|
||||
@@ -339,14 +436,17 @@ func (c *applicationUsecaseImpl) genPolicyByEnv(ctx context.Context, app *model.
|
||||
}
|
||||
appPolicy.Name = genPolicyName(envBinding.Name)
|
||||
appPolicy.Type = string(EnvBindingPolicy)
|
||||
|
||||
env, err := c.envUsecase.GetEnv(ctx, envName)
|
||||
if err != nil {
|
||||
return appPolicy, err
|
||||
}
|
||||
var envBindingSpec v1alpha1.EnvBindingSpec
|
||||
for _, targetName := range envBinding.TargetNames {
|
||||
target, err := c.deliveryTargetUsecase.GetDeliveryTarget(ctx, targetName)
|
||||
for _, targetName := range env.Targets {
|
||||
target, err := c.targetUsecase.GetTarget(ctx, targetName)
|
||||
if err != nil || target == nil {
|
||||
return appPolicy, bcode.ErrFoundEnvbindingDeliveryTarget
|
||||
}
|
||||
envBindingSpec.Envs = append(envBindingSpec.Envs, c.createTargetClusterEnv(ctx, app, envBinding, target, components))
|
||||
envBindingSpec.Envs = append(envBindingSpec.Envs, c.createTargetClusterEnv(ctx, envBinding, env, target, components))
|
||||
}
|
||||
properties, err := model.NewJSONStructByStruct(envBindingSpec)
|
||||
if err != nil {
|
||||
@@ -375,45 +475,6 @@ func (c *applicationUsecaseImpl) UpdateApplication(ctx context.Context, app *mod
|
||||
return c.converAppModelToBase(ctx, app), nil
|
||||
}
|
||||
|
||||
func (c *applicationUsecaseImpl) saveApplicationComponent(ctx context.Context, app *model.Application, components []common.ApplicationComponent) error {
|
||||
var componentModels []datastore.Entity
|
||||
for _, component := range components {
|
||||
// TODO: Check whether the component type is supported.
|
||||
var traits []model.ApplicationTrait
|
||||
for _, trait := range component.Traits {
|
||||
properties, err := model.NewJSONStruct(trait.Properties)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("parse trait properties failire %w", err)
|
||||
return bcode.ErrInvalidProperties
|
||||
}
|
||||
traits = append(traits, model.ApplicationTrait{
|
||||
Type: trait.Type,
|
||||
Properties: properties,
|
||||
})
|
||||
}
|
||||
properties, err := model.NewJSONStruct(component.Properties)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("parse component properties failire %w", err)
|
||||
return bcode.ErrInvalidProperties
|
||||
}
|
||||
componentModel := model.ApplicationComponent{
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
Name: component.Name,
|
||||
Type: component.Type,
|
||||
ExternalRevision: component.ExternalRevision,
|
||||
DependsOn: component.DependsOn,
|
||||
Inputs: component.Inputs,
|
||||
Outputs: component.Outputs,
|
||||
Scopes: component.Scopes,
|
||||
Traits: traits,
|
||||
Properties: properties,
|
||||
}
|
||||
componentModels = append(componentModels, &componentModel)
|
||||
}
|
||||
log.Logger.Infof("batch add %d components for app %s", len(componentModels), utils2.Sanitize(app.PrimaryKey()))
|
||||
return c.ds.BatchAdd(ctx, componentModels)
|
||||
}
|
||||
|
||||
// ListRecords list application record
|
||||
func (c *applicationUsecaseImpl) ListRecords(ctx context.Context, appName string) (*apisv1.ListWorkflowRecordsResponse, error) {
|
||||
var record = model.WorkflowRecord{
|
||||
@@ -520,52 +581,18 @@ func (c *applicationUsecaseImpl) converComponentModelToBase(m *model.Application
|
||||
|
||||
// ListPolicies list application policies
|
||||
func (c *applicationUsecaseImpl) ListPolicies(ctx context.Context, app *model.Application) ([]*apisv1.PolicyBase, error) {
|
||||
policies, err := c.queryApplicationPolicys(ctx, app)
|
||||
policies, err := c.queryApplicationPolicies(ctx, app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []*apisv1.PolicyBase
|
||||
for _, policy := range policies {
|
||||
list = append(list, c.converPolicyModelToBase(policy))
|
||||
list = append(list, convertPolicyModelToBase(policy))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (c *applicationUsecaseImpl) converPolicyModelToBase(policy *model.ApplicationPolicy) *apisv1.PolicyBase {
|
||||
pb := &apisv1.PolicyBase{
|
||||
Name: policy.Name,
|
||||
Type: policy.Type,
|
||||
Properties: policy.Properties,
|
||||
Description: policy.Description,
|
||||
Creator: policy.Creator,
|
||||
CreateTime: policy.CreateTime,
|
||||
UpdateTime: policy.UpdateTime,
|
||||
}
|
||||
return pb
|
||||
}
|
||||
|
||||
func (c *applicationUsecaseImpl) saveApplicationPolicy(ctx context.Context, app *model.Application, policys []v1beta1.AppPolicy) error {
|
||||
var policyModels []datastore.Entity
|
||||
for _, policy := range policys {
|
||||
properties, err := model.NewJSONStruct(policy.Properties)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("parse trait properties failire %w", err)
|
||||
return bcode.ErrInvalidProperties
|
||||
}
|
||||
appPolicy := &model.ApplicationPolicy{
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
Name: policy.Name,
|
||||
Type: policy.Type,
|
||||
Properties: properties,
|
||||
}
|
||||
if policy.Type != string(EnvBindingPolicy) {
|
||||
policyModels = append(policyModels, appPolicy)
|
||||
}
|
||||
}
|
||||
return c.ds.BatchAdd(ctx, policyModels)
|
||||
}
|
||||
|
||||
func (c *applicationUsecaseImpl) queryApplicationPolicys(ctx context.Context, app *model.Application) (list []*model.ApplicationPolicy, err error) {
|
||||
func (c *applicationUsecaseImpl) queryApplicationPolicies(ctx context.Context, app *model.Application) (list []*model.ApplicationPolicy, err error) {
|
||||
var policy = model.ApplicationPolicy{
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
}
|
||||
@@ -592,7 +619,7 @@ func (c *applicationUsecaseImpl) DetailPolicy(ctx context.Context, app *model.Ap
|
||||
return nil, err
|
||||
}
|
||||
return &apisv1.DetailPolicyResponse{
|
||||
PolicyBase: *c.converPolicyModelToBase(&policy),
|
||||
PolicyBase: *convertPolicyModelToBase(&policy),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -658,6 +685,7 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat
|
||||
TriggerType: req.TriggerType,
|
||||
WorkflowName: oamApp.Annotations[oam.AnnotationWorkflowName],
|
||||
EnvName: workflow.EnvName,
|
||||
CodeInfo: req.CodeInfo,
|
||||
}
|
||||
|
||||
if err := c.ds.Add(ctx, appRevision); err != nil {
|
||||
@@ -697,14 +725,7 @@ func (c *applicationUsecaseImpl) Deploy(ctx context.Context, app *model.Applicat
|
||||
}
|
||||
|
||||
return &apisv1.ApplicationDeployResponse{
|
||||
ApplicationRevisionBase: apisv1.ApplicationRevisionBase{
|
||||
Version: appRevision.Version,
|
||||
Status: appRevision.Status,
|
||||
Reason: appRevision.Reason,
|
||||
DeployUser: appRevision.DeployUser,
|
||||
Note: appRevision.Note,
|
||||
TriggerType: appRevision.TriggerType,
|
||||
},
|
||||
ApplicationRevisionBase: c.converRevisionModelToBase(appRevision),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -727,7 +748,10 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo
|
||||
if workflow == nil || workflow.EnvName == "" {
|
||||
return nil, bcode.ErrWorkflowNotExist
|
||||
}
|
||||
|
||||
env, err := c.envUsecase.GetEnv(ctx, workflow.EnvName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
labels := make(map[string]string)
|
||||
for key, value := range appModel.Labels {
|
||||
labels[key] = value
|
||||
@@ -740,8 +764,8 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: convertAppName(appModel.Name, workflow.EnvName),
|
||||
Namespace: appModel.Namespace,
|
||||
Name: appModel.Name,
|
||||
Namespace: env.Namespace,
|
||||
Labels: labels,
|
||||
Annotations: map[string]string{
|
||||
oam.AnnotationDeployVersion: version,
|
||||
@@ -754,8 +778,8 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo
|
||||
}
|
||||
originalApp := &v1beta1.Application{}
|
||||
if err := c.kubeClient.Get(ctx, types.NamespacedName{
|
||||
Name: convertAppName(appModel.Name, workflow.EnvName),
|
||||
Namespace: appModel.Namespace,
|
||||
Name: appModel.Name,
|
||||
Namespace: env.Namespace,
|
||||
}, originalApp); err == nil {
|
||||
app.ResourceVersion = originalApp.ResourceVersion
|
||||
}
|
||||
@@ -793,7 +817,7 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo
|
||||
traits = append(traits, aTrait)
|
||||
}
|
||||
bc := common.ApplicationComponent{
|
||||
Name: converComponentName(component.Name, workflow.EnvName),
|
||||
Name: component.Name,
|
||||
Type: component.Type,
|
||||
ExternalRevision: component.ExternalRevision,
|
||||
DependsOn: component.DependsOn,
|
||||
@@ -868,6 +892,20 @@ func (c *applicationUsecaseImpl) converAppModelToBase(ctx context.Context, app *
|
||||
return appBase
|
||||
}
|
||||
|
||||
func (c *applicationUsecaseImpl) converRevisionModelToBase(revision *model.ApplicationRevision) apisv1.ApplicationRevisionBase {
|
||||
return apisv1.ApplicationRevisionBase{
|
||||
Version: revision.Version,
|
||||
Status: revision.Status,
|
||||
Reason: revision.Reason,
|
||||
DeployUser: revision.DeployUser,
|
||||
Note: revision.Note,
|
||||
TriggerType: revision.TriggerType,
|
||||
CreateTime: revision.CreateTime,
|
||||
EnvName: revision.EnvName,
|
||||
CodeInfo: revision.CodeInfo,
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteApplication delete application
|
||||
func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *model.Application) error {
|
||||
// TODO: check app can be deleted
|
||||
@@ -897,6 +935,11 @@ func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *mod
|
||||
return err
|
||||
}
|
||||
|
||||
triggers, err := c.ListApplicationTriggers(ctx, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// delete workflow
|
||||
if err := c.workflowUsecase.DeleteWorkflowByApp(ctx, app); err != nil && !errors.Is(err, bcode.ErrWorkflowNotExist) {
|
||||
log.Logger.Errorf("delete workflow %s failure %s", app.Name, err.Error())
|
||||
@@ -923,6 +966,12 @@ func (c *applicationUsecaseImpl) DeleteApplication(ctx context.Context, app *mod
|
||||
}
|
||||
}
|
||||
|
||||
for _, trigger := range triggers {
|
||||
if err := c.ds.Delete(ctx, &model.ApplicationTrigger{AppPrimaryKey: app.PrimaryKey(), Name: trigger.Name, Token: trigger.Token}); err != nil {
|
||||
log.Logger.Errorf("delete trigger %s in app %s failure %s", trigger.Name, app.Name, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.envBindingUsecase.BatchDeleteEnvBinding(ctx, app); err != nil {
|
||||
log.Logger.Errorf("delete envbindings in app %s failure %s", app.Name, err.Error())
|
||||
}
|
||||
@@ -1127,7 +1176,7 @@ func (c *applicationUsecaseImpl) UpdatePolicy(ctx context.Context, app *model.Ap
|
||||
return nil, err
|
||||
}
|
||||
return &apisv1.DetailPolicyResponse{
|
||||
PolicyBase: *c.converPolicyModelToBase(&policy),
|
||||
PolicyBase: *convertPolicyModelToBase(&policy),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1230,16 +1279,7 @@ func (c *applicationUsecaseImpl) ListRevisions(ctx context.Context, appName, env
|
||||
for _, raw := range revisions {
|
||||
r, ok := raw.(*model.ApplicationRevision)
|
||||
if ok {
|
||||
resp.Revisions = append(resp.Revisions, apisv1.ApplicationRevisionBase{
|
||||
CreateTime: r.CreateTime,
|
||||
Version: r.Version,
|
||||
Status: r.Status,
|
||||
Reason: r.Reason,
|
||||
DeployUser: r.DeployUser,
|
||||
Note: r.Note,
|
||||
EnvName: r.EnvName,
|
||||
TriggerType: r.TriggerType,
|
||||
})
|
||||
resp.Revisions = append(resp.Revisions, c.converRevisionModelToBase(r))
|
||||
}
|
||||
}
|
||||
count, err := c.ds.Count(ctx, &revision, nil)
|
||||
@@ -1280,21 +1320,15 @@ func (c *applicationUsecaseImpl) Statistics(ctx context.Context, app *model.Appl
|
||||
return nil, err
|
||||
}
|
||||
return &apisv1.ApplicationStatisticsResponse{
|
||||
EnvCount: int64(len(envbinding)),
|
||||
DeliveryTargetCount: int64(len(targetMap)),
|
||||
RevisonCount: count,
|
||||
WorkflowCount: c.workflowUsecase.CountWorkflow(ctx, app),
|
||||
EnvCount: int64(len(envbinding)),
|
||||
TargetCount: int64(len(targetMap)),
|
||||
RevisonCount: count,
|
||||
WorkflowCount: c.workflowUsecase.CountWorkflow(ctx, app),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *applicationUsecaseImpl) createTargetClusterEnv(ctx context.Context, app *model.Application, envBind *model.EnvBinding, target *model.DeliveryTarget, components []*model.ApplicationComponent) v1alpha1.EnvConfig {
|
||||
func (c *applicationUsecaseImpl) createTargetClusterEnv(ctx context.Context, envBind *model.EnvBinding, env *model.Env, target *model.Target, components []*model.ApplicationComponent) v1alpha1.EnvConfig {
|
||||
placement := v1alpha1.EnvPlacement{}
|
||||
var componentSelector *v1alpha1.EnvSelector
|
||||
if envBind.ComponentSelector != nil {
|
||||
componentSelector = &v1alpha1.EnvSelector{
|
||||
Components: envBind.ComponentSelector.Components,
|
||||
}
|
||||
}
|
||||
if target.Cluster != nil {
|
||||
placement.ClusterSelector = &common.ClusterSelector{Name: target.Cluster.ClusterName}
|
||||
placement.NamespaceSelector = &v1alpha1.NamespaceSelector{Name: target.Cluster.Namespace}
|
||||
@@ -1302,7 +1336,7 @@ func (c *applicationUsecaseImpl) createTargetClusterEnv(ctx context.Context, app
|
||||
var componentPatchs []v1alpha1.EnvComponentPatch
|
||||
// init cloud application region and provider info
|
||||
for _, component := range components {
|
||||
definition, err := c.definitionUsecase.GetComponentDefinition(ctx, component.Type)
|
||||
definition, err := GetComponentDefinition(ctx, c.kubeClient, component.Type)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("get component definition %s failure %s", component.Type, err.Error())
|
||||
continue
|
||||
@@ -1316,7 +1350,7 @@ func (c *applicationUsecaseImpl) createTargetClusterEnv(ctx context.Context, app
|
||||
},
|
||||
"writeConnectionSecretToRef": map[string]interface{}{
|
||||
"name": fmt.Sprintf("%s-%s", component.Name, envBind.Name),
|
||||
"namespace": app.Namespace,
|
||||
"namespace": env.Namespace,
|
||||
},
|
||||
}
|
||||
if region, ok := target.Variable["region"]; ok {
|
||||
@@ -1329,7 +1363,7 @@ func (c *applicationUsecaseImpl) createTargetClusterEnv(ctx context.Context, app
|
||||
properties["providerRef"].(map[string]interface{})["namespace"] = providerNamespace
|
||||
}
|
||||
componentPatchs = append(componentPatchs, v1alpha1.EnvComponentPatch{
|
||||
Name: converComponentName(component.Name, envBind.Name),
|
||||
Name: component.Name,
|
||||
Properties: properties.RawExtension(),
|
||||
Type: component.Type,
|
||||
})
|
||||
@@ -1340,21 +1374,12 @@ func (c *applicationUsecaseImpl) createTargetClusterEnv(ctx context.Context, app
|
||||
return v1alpha1.EnvConfig{
|
||||
Name: genPolicyEnvName(target.Name),
|
||||
Placement: placement,
|
||||
Selector: componentSelector,
|
||||
Patch: v1alpha1.EnvPatch{
|
||||
Components: componentPatchs,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func convertAppName(appModelName, envName string) string {
|
||||
return fmt.Sprintf("%s-%s", appModelName, envName)
|
||||
}
|
||||
|
||||
func converComponentName(componentModelName, envName string) string {
|
||||
return fmt.Sprintf("%s-%s", componentModelName, envName)
|
||||
}
|
||||
|
||||
func genPolicyName(envName string) string {
|
||||
return fmt.Sprintf("%s-%s", EnvBindingPolicyDefaultName, envName)
|
||||
}
|
||||
@@ -1362,3 +1387,14 @@ func genPolicyName(envName string) string {
|
||||
func genPolicyEnvName(targetName string) string {
|
||||
return targetName
|
||||
}
|
||||
|
||||
func genWebhookToken() string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
runes := []rune("abcdefghijklmnopqrstuvwxyz0123456789")
|
||||
|
||||
b := make([]rune, defaultTokenLen)
|
||||
for i := range b {
|
||||
b[i] = runes[rand.Intn(len(runes))] // #nosec
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ package usecase
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
@@ -45,230 +46,164 @@ import (
|
||||
|
||||
var _ = Describe("Test application usecase function", func() {
|
||||
var (
|
||||
appUsecase *applicationUsecaseImpl
|
||||
workflowUsecase *workflowUsecaseImpl
|
||||
envBindingUsecase *envBindingUsecaseImpl
|
||||
deliveryTargetUsecase *deliveryTargetUsecaseImpl
|
||||
definitionUsecase *definitionUsecaseImpl
|
||||
projectUsecase *projectUsecaseImpl
|
||||
testProject = "app-project"
|
||||
appUsecase *applicationUsecaseImpl
|
||||
workflowUsecase *workflowUsecaseImpl
|
||||
envUsecase *envUsecaseImpl
|
||||
envBindingUsecase *envBindingUsecaseImpl
|
||||
targetUsecase *targetUsecaseImpl
|
||||
definitionUsecase *definitionUsecaseImpl
|
||||
projectUsecase *projectUsecaseImpl
|
||||
testProject = "app-project"
|
||||
testApp = "test-app"
|
||||
defaultTarget = "default"
|
||||
namespace1 = "app-test1"
|
||||
envnsdev = "envnsdev"
|
||||
envnstest = "envnstest"
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
workflowUsecase = &workflowUsecaseImpl{ds: ds}
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "app-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
workflowUsecase = &workflowUsecaseImpl{ds: ds, envUsecase: envUsecase}
|
||||
definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient}
|
||||
envBindingUsecase = &envBindingUsecaseImpl{ds: ds, workflowUsecase: workflowUsecase, kubeClient: k8sClient, definitionUsecase: definitionUsecase}
|
||||
deliveryTargetUsecase = &deliveryTargetUsecaseImpl{ds: ds}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
envBindingUsecase = &envBindingUsecaseImpl{ds: ds, envUsecase: envUsecase, workflowUsecase: workflowUsecase, kubeClient: k8sClient, definitionUsecase: definitionUsecase}
|
||||
targetUsecase = &targetUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
appUsecase = &applicationUsecaseImpl{
|
||||
ds: ds,
|
||||
workflowUsecase: workflowUsecase,
|
||||
apply: apply.NewAPIApplicator(k8sClient),
|
||||
kubeClient: k8sClient,
|
||||
envBindingUsecase: envBindingUsecase,
|
||||
definitionUsecase: definitionUsecase,
|
||||
deliveryTargetUsecase: deliveryTargetUsecase,
|
||||
projectUsecase: projectUsecase,
|
||||
ds: ds,
|
||||
workflowUsecase: workflowUsecase,
|
||||
apply: apply.NewAPIApplicator(k8sClient),
|
||||
kubeClient: k8sClient,
|
||||
envBindingUsecase: envBindingUsecase,
|
||||
envUsecase: envUsecase,
|
||||
definitionUsecase: definitionUsecase,
|
||||
targetUsecase: targetUsecase,
|
||||
projectUsecase: projectUsecase,
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
It("Test CreateApplication function", func() {
|
||||
|
||||
By("test sample create")
|
||||
_, err := projectUsecase.CreateProject(context.TODO(), v1.CreateProjectRequest{Name: testProject})
|
||||
_, err := targetUsecase.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: defaultTarget, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: namespace1}})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = projectUsecase.CreateProject(context.TODO(), v1.CreateProjectRequest{Name: testProject})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = envUsecase.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-dev", Namespace: envnsdev, Targets: []string{defaultTarget}, Project: "app-dev"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = envUsecase.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-test", Namespace: envnstest, Targets: []string{defaultTarget}, Project: "app-test"})
|
||||
Expect(err).Should(BeNil())
|
||||
req := v1.CreateApplicationRequest{
|
||||
Name: "test-app",
|
||||
Name: testApp,
|
||||
Project: testProject,
|
||||
Description: "this is a test app",
|
||||
EnvBinding: []*v1.EnvBinding{{
|
||||
Name: "dev",
|
||||
Description: "dev env",
|
||||
TargetNames: []string{"dev-target"},
|
||||
Name: "app-dev",
|
||||
}, {
|
||||
Name: "test",
|
||||
Description: "test env",
|
||||
TargetNames: []string{"test-target"},
|
||||
Name: "app-test",
|
||||
}},
|
||||
Component: &v1.CreateComponentRequest{
|
||||
Name: "component-name",
|
||||
ComponentType: "webservice",
|
||||
Properties: "{\"image\":\"nginx\"}",
|
||||
},
|
||||
}
|
||||
base, err := appUsecase.CreateApplication(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty())
|
||||
detail, err := appUsecase.DetailComponent(context.TODO(), &model.Application{Name: "test-app", Project: testProject}, "component-name")
|
||||
|
||||
triggers, err := appUsecase.ListApplicationTriggers(context.TODO(), &model.Application{Name: testApp})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(detail.Traits), 1)).Should(BeEmpty())
|
||||
|
||||
_, err = appUsecase.CreateApplication(context.TODO(), req)
|
||||
equal := cmp.Equal(err, bcode.ErrApplicationExist, cmpopts.EquateErrors())
|
||||
Expect(equal).Should(BeTrue())
|
||||
|
||||
By("test with oam yaml config create")
|
||||
bs, err := ioutil.ReadFile("./testdata/example-app.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
req = v1.CreateApplicationRequest{
|
||||
Name: "test-app-sadasd",
|
||||
Project: testProject,
|
||||
Description: "this is a test app",
|
||||
Icon: "",
|
||||
Labels: map[string]string{"test": "true"},
|
||||
YamlConfig: string(bs),
|
||||
}
|
||||
base, err = appUsecase.CreateApplication(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty())
|
||||
|
||||
req = v1.CreateApplicationRequest{
|
||||
Name: "test-app-sadasd2",
|
||||
Project: testProject,
|
||||
Description: "this is a test app",
|
||||
Icon: "",
|
||||
Labels: map[string]string{"test": "true"},
|
||||
YamlConfig: "asdasdasdasd",
|
||||
}
|
||||
base, err = appUsecase.CreateApplication(context.TODO(), req)
|
||||
equal = cmp.Equal(err, bcode.ErrApplicationConfig, cmpopts.EquateErrors())
|
||||
Expect(equal).Should(BeTrue())
|
||||
Expect(base).Should(BeNil())
|
||||
|
||||
bs, err = ioutil.ReadFile("./testdata/example-app-error.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
req = v1.CreateApplicationRequest{
|
||||
Name: "test-app-sadasd3",
|
||||
Project: testProject,
|
||||
Description: "this is a test app",
|
||||
Icon: "",
|
||||
Labels: map[string]string{"test": "true"},
|
||||
YamlConfig: string(bs),
|
||||
}
|
||||
_, err = appUsecase.CreateApplication(context.TODO(), req)
|
||||
equal = cmp.Equal(err, bcode.ErrInvalidProperties, cmpopts.EquateErrors())
|
||||
Expect(equal).Should(BeTrue())
|
||||
|
||||
By("Test create app with env binding")
|
||||
req = v1.CreateApplicationRequest{
|
||||
Name: "test-app-sadasd4",
|
||||
Project: testProject,
|
||||
Description: "this is a test app",
|
||||
Icon: "",
|
||||
Labels: map[string]string{"test": "true"},
|
||||
EnvBinding: []*v1.EnvBinding{
|
||||
{
|
||||
Name: "dev",
|
||||
Alias: "Chinese Word",
|
||||
Description: "This is a dev env",
|
||||
TargetNames: []string{"dev-target"},
|
||||
},
|
||||
{
|
||||
Name: "prod",
|
||||
Description: "This is a prod env",
|
||||
TargetNames: []string{"prod-target"},
|
||||
},
|
||||
},
|
||||
}
|
||||
appBase, err := appUsecase.CreateApplication(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appBase.Name, "test-app-sadasd4")).Should(BeEmpty())
|
||||
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd4")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
|
||||
Expect(len(triggers)).Should(Equal(1))
|
||||
})
|
||||
|
||||
It("Test ListApplications function", func() {
|
||||
_, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicatioOptions{})
|
||||
_, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicationOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test ListApplications and filter by targetName function", func() {
|
||||
list, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicatioOptions{
|
||||
list, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicationOptions{
|
||||
Project: testProject,
|
||||
TargetName: "dev-target"})
|
||||
TargetName: defaultTarget})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(list), 2)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(len(list), 1)).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test DetailApplication function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
|
||||
detail, err := appUsecase.DetailApplication(context.TODO(), appModel)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(detail.ResourceInfo.ComponentNum, int64(2))).Should(BeEmpty())
|
||||
Expect(cmp.Diff(detail.ResourceInfo.ComponentNum, int64(1))).Should(BeEmpty())
|
||||
Expect(cmp.Diff(len(detail.Policies), 0)).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test CreateTrigger function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = appUsecase.CreateApplicationTrigger(context.TODO(), appModel, v1.CreateApplicationTriggerRequest{
|
||||
Name: "trigger-name",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test ListTriggers function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
triggers, err := appUsecase.ListApplicationTriggers(context.TODO(), appModel)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(triggers)).Should(Equal(2))
|
||||
})
|
||||
|
||||
It("Test ListComponents function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
|
||||
components, err := appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(components), 2)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(components[0].ComponentType, "worker")).Should(BeEmpty())
|
||||
Expect(components[1].UpdateTime).ShouldNot(BeNil())
|
||||
|
||||
components, err = appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{
|
||||
EnvName: "test",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(components), 2)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(components[0].Name, "data-worker")).Should(BeEmpty())
|
||||
|
||||
components, err = appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{
|
||||
EnvName: "staging",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(components), 2)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(components[0].Name, "data-worker")).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test DetailComponent function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
|
||||
detail, err := appUsecase.DetailComponent(context.TODO(), appModel, "hello-world-server")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(detail.Traits), 1)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(detail.Type, "webservice")).Should(BeEmpty())
|
||||
Expect(cmp.Diff(strings.Contains((*detail.Properties)["image"].(string), "crccheck/hello-world"), true)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(len(components), 1)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(components[0].ComponentType, "webservice")).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test AddComponent function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
|
||||
base, err := appUsecase.AddComponent(context.TODO(), appModel, v1.CreateComponentRequest{
|
||||
Name: "test2",
|
||||
Description: "this is a test2 component",
|
||||
Labels: map[string]string{},
|
||||
ComponentType: "worker",
|
||||
Properties: `{"image": "busybox","cmd":["sleep", "1000"],"lives": "3","enemies": "alien"}`,
|
||||
DependsOn: []string{"data-worker"},
|
||||
DependsOn: []string{"component-name"},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.ComponentType, "worker")).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test DetailComponent function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
detailResponse, err := appUsecase.DetailComponent(context.TODO(), appModel, "test2")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(detailResponse.DependsOn[0], "data-worker")).Should(BeEmpty())
|
||||
Expect(cmp.Diff(detailResponse.DependsOn[0], "component-name")).Should(BeEmpty())
|
||||
Expect(detailResponse.Properties).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff((*detailResponse.Properties)["image"], "busybox")).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test AddPolicy function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
_, err = appUsecase.AddPolicy(context.TODO(), appModel, v1.CreatePolicyRequest{
|
||||
@@ -289,7 +224,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test ListPolicies function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
|
||||
@@ -299,7 +234,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test DetailPolicy function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
detail, err := appUsecase.DetailPolicy(context.TODO(), appModel, EnvBindingPolicyDefaultName)
|
||||
@@ -309,7 +244,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test UpdatePolicy function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
base, err := appUsecase.UpdatePolicy(context.TODO(), appModel, EnvBindingPolicyDefaultName, v1.UpdatePolicyRequest{
|
||||
@@ -321,7 +256,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
Expect((*base.Properties)["envs"]).Should(BeEmpty())
|
||||
})
|
||||
It("Test DeletePolicy function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
err = appUsecase.DeletePolicy(context.TODO(), appModel, EnvBindingPolicyDefaultName)
|
||||
@@ -329,7 +264,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test add application trait", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
alias := "alias"
|
||||
description := "description"
|
||||
@@ -351,7 +286,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test add application a dup trait", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = appUsecase.CreateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, v1.CreateApplicationTraitRequest{
|
||||
Type: "Ingress",
|
||||
@@ -361,7 +296,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test update application trait", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
alias := "newAlias"
|
||||
description := "newDescription"
|
||||
@@ -382,7 +317,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test update a not exist", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = appUsecase.UpdateApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, "Ingress-1-20", v1.UpdateApplicationTraitRequest{
|
||||
Properties: `{"domain":"www.test1.com"}`,
|
||||
@@ -391,7 +326,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test delete an exist trait", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
err = appUsecase.DeleteApplicationTrait(context.TODO(), appModel, &model.ApplicationComponent{Name: "test2"}, "Ingress")
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -402,30 +337,17 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test DeleteComponent function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(appModel.Project, testProject)).Should(BeEmpty())
|
||||
err = appUsecase.DeleteComponent(context.TODO(), appModel, "test2")
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test DeleteApplication function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-sadasd")
|
||||
Expect(err).Should(BeNil())
|
||||
err = appUsecase.DeleteApplication(context.TODO(), appModel)
|
||||
Expect(err).Should(BeNil())
|
||||
components, err := appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(components), 0)).Should(BeEmpty())
|
||||
policies, err := appUsecase.ListPolicies(context.TODO(), appModel)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(policies), 0)).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test ListRevisions function", func() {
|
||||
for i := 0; i < 3; i++ {
|
||||
appModel := &model.ApplicationRevision{
|
||||
AppPrimaryKey: "test-app",
|
||||
AppPrimaryKey: "test-app-sadasd",
|
||||
Version: fmt.Sprintf("%d", i),
|
||||
EnvName: fmt.Sprintf("env-%d", i),
|
||||
Status: model.RevisionStatusRunning,
|
||||
@@ -436,15 +358,15 @@ var _ = Describe("Test application usecase function", func() {
|
||||
err := workflowUsecase.createTestApplicationRevision(context.TODO(), appModel)
|
||||
Expect(err).Should(BeNil())
|
||||
}
|
||||
revisions, err := appUsecase.ListRevisions(context.TODO(), "test-app", "", "", 0, 10)
|
||||
revisions, err := appUsecase.ListRevisions(context.TODO(), "test-app-sadasd", "", "", 0, 10)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(revisions.Total).Should(Equal(int64(3)))
|
||||
|
||||
revisions, err = appUsecase.ListRevisions(context.TODO(), "test-app", "env-0", "", 0, 10)
|
||||
revisions, err = appUsecase.ListRevisions(context.TODO(), "test-app-sadasd", "env-0", "", 0, 10)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(revisions.Total).Should(Equal(int64(1)))
|
||||
|
||||
revisions, err = appUsecase.ListRevisions(context.TODO(), "test-app", "", "terminated", 0, 10)
|
||||
revisions, err = appUsecase.ListRevisions(context.TODO(), "test-app-sadasd", "", "terminated", 0, 10)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(revisions.Total).Should(Equal(int64(1)))
|
||||
|
||||
@@ -467,19 +389,13 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test ApplicationEnvRecycle function", func() {
|
||||
req := v1.CreateApplicationRequest{
|
||||
Name: "app-env-recycle" + "-dev",
|
||||
Project: testProject,
|
||||
Description: "this is a test app with env",
|
||||
}
|
||||
base, err := appUsecase.CreateApplication(context.TODO(), req)
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = appUsecase.Deploy(context.TODO(), appModel, v1.ApplicationDeployRequest{WorkflowName: convertWorkflowName("app-dev")})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty())
|
||||
|
||||
err = envBindingUsecase.ApplicationEnvRecycle(context.TODO(), &model.Application{
|
||||
Name: "app-env-recycle",
|
||||
Namespace: "project-" + testProject,
|
||||
}, &model.EnvBinding{Name: "dev"})
|
||||
Name: testApp,
|
||||
}, &model.EnvBinding{Name: "app-dev"})
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
@@ -540,11 +456,11 @@ var _ = Describe("Test application usecase function", func() {
|
||||
}
|
||||
err = k8sClient.Create(context.TODO(), definition)
|
||||
Expect(err).Should(BeNil())
|
||||
envConfig := appUsecase.createTargetClusterEnv(context.TODO(), &model.Application{
|
||||
Namespace: "prod",
|
||||
}, &model.EnvBinding{
|
||||
TargetNames: []string{"prod"},
|
||||
}, &model.DeliveryTarget{
|
||||
envConfig := appUsecase.createTargetClusterEnv(context.TODO(), &model.EnvBinding{
|
||||
Name: "prod",
|
||||
}, &model.Env{
|
||||
Name: "prod",
|
||||
}, &model.Target{
|
||||
Name: "prod",
|
||||
Variable: map[string]interface{}{
|
||||
"region": "hangzhou",
|
||||
@@ -561,13 +477,27 @@ var _ = Describe("Test application usecase function", func() {
|
||||
err = k8sClient.Delete(context.TODO(), definition)
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test DeleteApplication function", func() {
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), testApp)
|
||||
Expect(err).Should(BeNil())
|
||||
time.Sleep(time.Second * 3)
|
||||
err = appUsecase.DeleteApplication(context.TODO(), appModel)
|
||||
Expect(err).Should(BeNil())
|
||||
components, err := appUsecase.ListComponents(context.TODO(), appModel, v1.ListApplicationComponentOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(components), 0)).Should(BeEmpty())
|
||||
policies, err := appUsecase.ListPolicies(context.TODO(), appModel)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(policies), 0)).Should(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
||||
func createTestSuspendApp(ctx context.Context, appName, envName, revisionVersion, wfName, recordName string, kubeClient client.Client) (*v1beta1.Application, error) {
|
||||
testapp := &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: convertAppName(appName, envName),
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
Namespace: envName,
|
||||
Annotations: map[string]string{
|
||||
oam.AnnotationDeployVersion: revisionVersion,
|
||||
oam.AnnotationWorkflowName: wfName,
|
||||
@@ -578,8 +508,8 @@ func createTestSuspendApp(ctx context.Context, appName, envName, revisionVersion
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{{
|
||||
Name: "test-component",
|
||||
Type: "worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"test":"test"}`)},
|
||||
Type: "webservice",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"image":"nginx"}`)},
|
||||
Traits: []common.ApplicationTrait{},
|
||||
Scopes: map[string]string{},
|
||||
}},
|
||||
|
||||
@@ -162,7 +162,7 @@ func (c *clusterUsecaseImpl) ListKubeClusters(ctx context.Context, query string,
|
||||
clusters, err := c.ds.List(ctx, &model.Cluster{}, &datastore.ListOptions{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
SortBy: []datastore.SortOption{{Key: "model.createTime", Order: datastore.SortOrderDescending}},
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
FilterOptions: fo,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -174,7 +174,12 @@ func (c *clusterUsecaseImpl) ListKubeClusters(ctx context.Context, query string,
|
||||
for _, raw := range clusters {
|
||||
cluster, ok := raw.(*model.Cluster)
|
||||
if ok {
|
||||
resp.Clusters = append(resp.Clusters, *newClusterBaseFromCluster(cluster))
|
||||
// local cluster must be first
|
||||
if cluster.Name == multicluster.ClusterLocalName {
|
||||
resp.Clusters = append([]apis.ClusterBase{*newClusterBaseFromCluster(cluster)}, resp.Clusters...)
|
||||
} else {
|
||||
resp.Clusters = append(resp.Clusters, *newClusterBaseFromCluster(cluster))
|
||||
}
|
||||
}
|
||||
}
|
||||
total, err := c.ds.Count(ctx, &model.Cluster{}, &fo)
|
||||
|
||||
77
pkg/apiserver/rest/usecase/converter.go
Normal file
77
pkg/apiserver/rest/usecase/converter.go
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
func convertPolicyModelToBase(policy *model.ApplicationPolicy) *apisv1.PolicyBase {
|
||||
pb := &apisv1.PolicyBase{
|
||||
Name: policy.Name,
|
||||
Type: policy.Type,
|
||||
Properties: policy.Properties,
|
||||
Description: policy.Description,
|
||||
Creator: policy.Creator,
|
||||
CreateTime: policy.CreateTime,
|
||||
UpdateTime: policy.UpdateTime,
|
||||
}
|
||||
return pb
|
||||
}
|
||||
|
||||
func convertWorkflowBase(workflow *model.Workflow) apisv1.WorkflowBase {
|
||||
var steps []apisv1.WorkflowStep
|
||||
for _, step := range workflow.Steps {
|
||||
steps = append(steps, convertFromWorkflowStepModel(step))
|
||||
}
|
||||
return apisv1.WorkflowBase{
|
||||
Name: workflow.Name,
|
||||
Alias: workflow.Alias,
|
||||
Description: workflow.Description,
|
||||
Default: convertBool(workflow.Default),
|
||||
EnvName: workflow.EnvName,
|
||||
CreateTime: workflow.CreateTime,
|
||||
UpdateTime: workflow.UpdateTime,
|
||||
Steps: steps,
|
||||
}
|
||||
}
|
||||
|
||||
// convertAPIStep2ModelStep will convert api types of workflow step to model type
|
||||
func convertAPIStep2ModelStep(apiSteps []apisv1.WorkflowStep) ([]model.WorkflowStep, error) {
|
||||
var steps []model.WorkflowStep
|
||||
for _, step := range apiSteps {
|
||||
properties, err := model.NewJSONStructByString(step.Properties)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("parse trait properties failire %w", err)
|
||||
return nil, bcode.ErrInvalidProperties
|
||||
}
|
||||
steps = append(steps, model.WorkflowStep{
|
||||
Name: step.Name,
|
||||
Alias: step.Alias,
|
||||
Description: step.Description,
|
||||
DependsOn: step.DependsOn,
|
||||
Type: step.Type,
|
||||
Inputs: step.Inputs,
|
||||
Outputs: step.Outputs,
|
||||
Properties: properties,
|
||||
})
|
||||
}
|
||||
return steps, nil
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
@@ -52,8 +51,6 @@ type DefinitionUsecase interface {
|
||||
DetailDefinition(ctx context.Context, name, defType string) (*apisv1.DetailDefinitionResponse, error)
|
||||
// AddDefinitionUISchema add or update custom definition ui schema
|
||||
AddDefinitionUISchema(ctx context.Context, name, defType, configRaw string) ([]*utils.UIParameter, error)
|
||||
// GetComponentDefinition get component definition
|
||||
GetComponentDefinition(ctx context.Context, name string) (*v1alpha2.ComponentDefinition, error)
|
||||
}
|
||||
|
||||
type definitionUsecaseImpl struct {
|
||||
@@ -162,14 +159,6 @@ func (d *definitionUsecaseImpl) listDefinitions(ctx context.Context, list *unstr
|
||||
return defs, nil
|
||||
}
|
||||
|
||||
func (d *definitionUsecaseImpl) GetComponentDefinition(ctx context.Context, name string) (*v1alpha2.ComponentDefinition, error) {
|
||||
var componentDefinition v1alpha2.ComponentDefinition
|
||||
if err := d.kubeClient.Get(ctx, k8stypes.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: name}, &componentDefinition); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &componentDefinition, nil
|
||||
}
|
||||
|
||||
// DetailDefinition get definition detail
|
||||
func (d *definitionUsecaseImpl) DetailDefinition(ctx context.Context, name, defType string) (*apisv1.DetailDefinitionResponse, error) {
|
||||
if !utils.StringsContain([]string{"component", "trait", "workflowstep"}, defType) {
|
||||
@@ -274,6 +263,9 @@ func patchSchema(defaultSchema, customSchema []*utils.UIParameter) []*utils.UIPa
|
||||
for i, custom := range customSchema {
|
||||
customSchemaMap[custom.JSONKey] = customSchema[i]
|
||||
}
|
||||
if len(defaultSchema) == 0 {
|
||||
return customSchema
|
||||
}
|
||||
for i := range defaultSchema {
|
||||
dSchema := defaultSchema[i]
|
||||
if cusSchema, exist := customSchemaMap[dSchema.JSONKey]; exist {
|
||||
|
||||
@@ -1,199 +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 usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
// DeliveryTargetUsecase deliveryTarget manage api
|
||||
type DeliveryTargetUsecase interface {
|
||||
GetDeliveryTarget(ctx context.Context, deliveryTargetName string) (*model.DeliveryTarget, error)
|
||||
DetailDeliveryTarget(ctx context.Context, deliveryTarget *model.DeliveryTarget) (*apisv1.DetailDeliveryTargetResponse, error)
|
||||
DeleteDeliveryTarget(ctx context.Context, deliveryTargetName string) error
|
||||
CreateDeliveryTarget(ctx context.Context, req apisv1.CreateDeliveryTargetRequest) (*apisv1.DetailDeliveryTargetResponse, error)
|
||||
UpdateDeliveryTarget(ctx context.Context, deliveryTarget *model.DeliveryTarget, req apisv1.UpdateDeliveryTargetRequest) (*apisv1.DetailDeliveryTargetResponse, error)
|
||||
ListDeliveryTargets(ctx context.Context, page, pageSize int, project string) (*apisv1.ListTargetResponse, error)
|
||||
}
|
||||
|
||||
type deliveryTargetUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
projectUsecase ProjectUsecase
|
||||
}
|
||||
|
||||
// NewDeliveryTargetUsecase new DeliveryTarget usecase
|
||||
func NewDeliveryTargetUsecase(ds datastore.DataStore, projectUsecase ProjectUsecase) DeliveryTargetUsecase {
|
||||
return &deliveryTargetUsecaseImpl{
|
||||
ds: ds,
|
||||
projectUsecase: projectUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *deliveryTargetUsecaseImpl) ListDeliveryTargets(ctx context.Context, page, pageSize int, project string) (*apisv1.ListTargetResponse, error) {
|
||||
deliveryTarget := model.DeliveryTarget{}
|
||||
if project != "" {
|
||||
deliveryTarget.Project = project
|
||||
}
|
||||
deliveryTargets, err := dt.ds.List(ctx, &deliveryTarget, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &apisv1.ListTargetResponse{
|
||||
Targets: []apisv1.DeliveryTargetBase{},
|
||||
}
|
||||
for _, raw := range deliveryTargets {
|
||||
target, ok := raw.(*model.DeliveryTarget)
|
||||
if ok {
|
||||
resp.Targets = append(resp.Targets, *(dt.convertFromDeliveryTargetModel(ctx, target)))
|
||||
}
|
||||
}
|
||||
count, err := dt.ds.Count(ctx, &deliveryTarget, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Total = count
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DeleteDeliveryTarget delete application DeliveryTarget
|
||||
func (dt *deliveryTargetUsecaseImpl) DeleteDeliveryTarget(ctx context.Context, deliveryTargetName string) error {
|
||||
deliveryTarget := &model.DeliveryTarget{
|
||||
Name: deliveryTargetName,
|
||||
}
|
||||
if err := dt.ds.Delete(ctx, deliveryTarget); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return bcode.ErrDeliveryTargetNotExist
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dt *deliveryTargetUsecaseImpl) CreateDeliveryTarget(ctx context.Context, req apisv1.CreateDeliveryTargetRequest) (*apisv1.DetailDeliveryTargetResponse, error) {
|
||||
deliveryTarget := convertCreateReqToDeliveryTargetModel(req)
|
||||
|
||||
// check deliveryTarget name.
|
||||
exit, err := dt.ds.IsExist(ctx, &deliveryTarget)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("check application name is exist failure %s", err.Error())
|
||||
return nil, bcode.ErrDeliveryTargetExist
|
||||
}
|
||||
if exit {
|
||||
return nil, bcode.ErrDeliveryTargetExist
|
||||
}
|
||||
// check project
|
||||
project, err := dt.projectUsecase.GetProject(ctx, req.Project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deliveryTarget.Namespace = project.Namespace
|
||||
deliveryTarget.Project = project.Name
|
||||
|
||||
if err := dt.ds.Add(ctx, &deliveryTarget); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dt.DetailDeliveryTarget(ctx, &deliveryTarget)
|
||||
}
|
||||
|
||||
func (dt *deliveryTargetUsecaseImpl) UpdateDeliveryTarget(ctx context.Context, deliveryTarget *model.DeliveryTarget, req apisv1.UpdateDeliveryTargetRequest) (*apisv1.DetailDeliveryTargetResponse, error) {
|
||||
deliveryTargetModel := convertUpdateReqToDeliveryTargetModel(deliveryTarget, req)
|
||||
if err := dt.ds.Put(ctx, deliveryTargetModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dt.DetailDeliveryTarget(ctx, deliveryTargetModel)
|
||||
}
|
||||
|
||||
// DetailDeliveryTarget detail DeliveryTarget
|
||||
func (dt *deliveryTargetUsecaseImpl) DetailDeliveryTarget(ctx context.Context, deliveryTarget *model.DeliveryTarget) (*apisv1.DetailDeliveryTargetResponse, error) {
|
||||
return &apisv1.DetailDeliveryTargetResponse{
|
||||
DeliveryTargetBase: *dt.convertFromDeliveryTargetModel(ctx, deliveryTarget),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetDeliveryTarget get DeliveryTarget model
|
||||
func (dt *deliveryTargetUsecaseImpl) GetDeliveryTarget(ctx context.Context, deliveryTargetName string) (*model.DeliveryTarget, error) {
|
||||
deliveryTarget := &model.DeliveryTarget{
|
||||
Name: deliveryTargetName,
|
||||
}
|
||||
if err := dt.ds.Get(ctx, deliveryTarget); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return deliveryTarget, nil
|
||||
}
|
||||
|
||||
func convertUpdateReqToDeliveryTargetModel(deliveryTarget *model.DeliveryTarget, req apisv1.UpdateDeliveryTargetRequest) *model.DeliveryTarget {
|
||||
deliveryTarget.Alias = req.Alias
|
||||
deliveryTarget.Description = req.Description
|
||||
deliveryTarget.Cluster = (*model.ClusterTarget)(req.Cluster)
|
||||
deliveryTarget.Variable = req.Variable
|
||||
return deliveryTarget
|
||||
}
|
||||
|
||||
func convertCreateReqToDeliveryTargetModel(req apisv1.CreateDeliveryTargetRequest) model.DeliveryTarget {
|
||||
deliveryTarget := model.DeliveryTarget{
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
Cluster: (*model.ClusterTarget)(req.Cluster),
|
||||
Variable: req.Variable,
|
||||
}
|
||||
return deliveryTarget
|
||||
}
|
||||
|
||||
func (dt *deliveryTargetUsecaseImpl) convertFromDeliveryTargetModel(ctx context.Context, deliveryTarget *model.DeliveryTarget) *apisv1.DeliveryTargetBase {
|
||||
var appNum int64 = 0
|
||||
// TODO: query app num in target
|
||||
targetBase := &apisv1.DeliveryTargetBase{
|
||||
Name: deliveryTarget.Name,
|
||||
Alias: deliveryTarget.Alias,
|
||||
Description: deliveryTarget.Description,
|
||||
Cluster: (*apisv1.ClusterTarget)(deliveryTarget.Cluster),
|
||||
Variable: deliveryTarget.Variable,
|
||||
CreateTime: deliveryTarget.CreateTime,
|
||||
UpdateTime: deliveryTarget.UpdateTime,
|
||||
AppNum: appNum,
|
||||
}
|
||||
|
||||
project, err := dt.projectUsecase.GetProject(ctx, deliveryTarget.Project)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("query project info failure %s", err.Error())
|
||||
}
|
||||
if project != nil {
|
||||
targetBase.Project = convertProjectModel2Base(project)
|
||||
}
|
||||
|
||||
if targetBase.Cluster != nil && targetBase.Cluster.ClusterName != "" {
|
||||
cluster, err := _getClusterFromDataStore(ctx, dt.ds, deliveryTarget.Cluster.ClusterName)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("query cluster info failure %s", err.Error())
|
||||
}
|
||||
if cluster != nil {
|
||||
targetBase.ClusterAlias = cluster.Alias
|
||||
}
|
||||
}
|
||||
|
||||
return targetBase
|
||||
}
|
||||
301
pkg/apiserver/rest/usecase/env.go
Normal file
301
pkg/apiserver/rest/usecase/env.go
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
apierror "k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
util "github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// EnvUsecase defines the API of Env.
|
||||
type EnvUsecase interface {
|
||||
GetEnv(ctx context.Context, envName string) (*model.Env, error)
|
||||
ListEnvs(ctx context.Context, page, pageSize int, listOption apisv1.ListEnvOptions) ([]*apisv1.Env, error)
|
||||
DeleteEnv(ctx context.Context, envName string) error
|
||||
CreateEnv(ctx context.Context, req apisv1.CreateEnvRequest) (*apisv1.Env, error)
|
||||
UpdateEnv(ctx context.Context, envName string, req apisv1.UpdateEnvRequest) (*apisv1.Env, error)
|
||||
}
|
||||
|
||||
type envUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
kubeClient client.Client
|
||||
}
|
||||
|
||||
// NewEnvUsecase new env usecase
|
||||
func NewEnvUsecase(ds datastore.DataStore) EnvUsecase {
|
||||
kubecli, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get kubeclient failure %s", err.Error())
|
||||
}
|
||||
return &envUsecaseImpl{kubeClient: kubecli, ds: ds}
|
||||
}
|
||||
|
||||
// GetEnv get env
|
||||
func (p *envUsecaseImpl) GetEnv(ctx context.Context, envName string) (*model.Env, error) {
|
||||
return getEnv(ctx, p.ds, envName)
|
||||
}
|
||||
|
||||
// DeleteEnv delete an env by name
|
||||
// the function assume applications contain in env already empty.
|
||||
// it won't delete the namespace created by the Env, but it will update the label
|
||||
func (p *envUsecaseImpl) DeleteEnv(ctx context.Context, envName string) error {
|
||||
env := &model.Env{}
|
||||
env.Name = envName
|
||||
|
||||
if err := p.ds.Get(ctx, env); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
// reset the labels
|
||||
err := util.UpdateNamespace(ctx, p.kubeClient, env.Namespace, util.MergeOverrideLabels(map[string]string{
|
||||
oam.LabelNamespaceOfEnvName: "",
|
||||
oam.LabelControlPlaneNamespaceUsage: "",
|
||||
}))
|
||||
if err != nil && apierror.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = p.ds.Delete(ctx, env); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListEnvs list envs
|
||||
func (p *envUsecaseImpl) ListEnvs(ctx context.Context, page, pageSize int, listOption apisv1.ListEnvOptions) ([]*apisv1.Env, error) {
|
||||
entities, err := listEnvs(ctx, p.ds, listOption.Project, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Targets, err := listTarget(ctx, p.ds, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var envs []*apisv1.Env
|
||||
for _, ee := range entities {
|
||||
envs = append(envs, convertEnvModel2Base(ee, Targets))
|
||||
}
|
||||
|
||||
projects, err := listProjects(ctx, p.ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, e := range envs {
|
||||
for _, pj := range projects {
|
||||
if e.Project.Name == pj.Name {
|
||||
e.Project.Alias = pj.Alias
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return envs, nil
|
||||
}
|
||||
|
||||
func checkEqual(old, new []string) bool {
|
||||
if old == nil && new == nil {
|
||||
return true
|
||||
}
|
||||
if old == nil || new == nil {
|
||||
return false
|
||||
}
|
||||
sort.Strings(old)
|
||||
sort.Strings(new)
|
||||
return reflect.DeepEqual(old, new)
|
||||
}
|
||||
|
||||
func (p *envUsecaseImpl) updateAppWithNewEnv(ctx context.Context, envName string, env *model.Env) error {
|
||||
|
||||
// List all apps inside the env
|
||||
apps, err := listApp(ctx, p.ds, apisv1.ListApplicationOptions{Env: envName})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, app := range apps {
|
||||
err = UpdateEnvWorkflow(ctx, p.kubeClient, p.ds, app, env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// UpdateEnv update an env for request
|
||||
func (p *envUsecaseImpl) UpdateEnv(ctx context.Context, name string, req apisv1.UpdateEnvRequest) (*apisv1.Env, error) {
|
||||
env := &model.Env{}
|
||||
env.Name = name
|
||||
err := p.ds.Get(ctx, env)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("check if env name exists failure %s", err.Error())
|
||||
return nil, bcode.ErrEnvNotExisted
|
||||
}
|
||||
if req.Alias != "" {
|
||||
env.Alias = req.Alias
|
||||
}
|
||||
if req.Description != "" {
|
||||
env.Description = req.Description
|
||||
}
|
||||
|
||||
pass, err := p.checkEnvTarget(ctx, env.Project, env.Name, req.Targets)
|
||||
if err != nil || !pass {
|
||||
return nil, bcode.ErrEnvTargetConflict
|
||||
}
|
||||
|
||||
var targetChanged bool
|
||||
if len(req.Targets) > 0 && !checkEqual(env.Targets, req.Targets) {
|
||||
targetChanged = true
|
||||
env.Targets = req.Targets
|
||||
}
|
||||
|
||||
targets, err := listTarget(ctx, p.ds, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var targetMap = make(map[string]*model.Target, len(targets))
|
||||
for i, existTarget := range targets {
|
||||
targetMap[existTarget.Name] = targets[i]
|
||||
}
|
||||
for _, target := range req.Targets {
|
||||
if _, exist := targetMap[target]; !exist {
|
||||
return nil, bcode.ErrTargetNotExist
|
||||
}
|
||||
}
|
||||
|
||||
// create namespace at first
|
||||
if err := p.ds.Put(ctx, env); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if targetChanged {
|
||||
if err = p.updateAppWithNewEnv(ctx, name, env); err != nil {
|
||||
log.Logger.Errorf("update envbinding failure %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
resp := convertEnvModel2Base(env, targets)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateEnv create an env for request
|
||||
func (p *envUsecaseImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequest) (*apisv1.Env, error) {
|
||||
newEnv := &model.Env{
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
Namespace: req.Namespace,
|
||||
Project: req.Project,
|
||||
Targets: req.Targets,
|
||||
}
|
||||
|
||||
pass, err := p.checkEnvTarget(ctx, req.Project, req.Name, req.Targets)
|
||||
if err != nil || !pass {
|
||||
return nil, bcode.ErrEnvTargetConflict
|
||||
}
|
||||
|
||||
targets, err := listTarget(ctx, p.ds, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var targetMap = make(map[string]*model.Target, len(targets))
|
||||
for i, existTarget := range targets {
|
||||
targetMap[existTarget.Name] = targets[i]
|
||||
}
|
||||
|
||||
for _, target := range req.Targets {
|
||||
if _, exist := targetMap[target]; !exist {
|
||||
return nil, bcode.ErrTargetNotExist
|
||||
}
|
||||
}
|
||||
|
||||
err = createEnv(ctx, p.kubeClient, p.ds, newEnv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := convertEnvModel2Base(newEnv, targets)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// checkEnvTarget In one project, a delivery target can only belong to one env.
|
||||
func (p *envUsecaseImpl) checkEnvTarget(ctx context.Context, project string, envName string, targets []string) (bool, error) {
|
||||
if len(targets) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
entitys, err := p.ds.List(ctx, &model.Env{Project: project}, &datastore.ListOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
newMap := make(map[string]bool, len(targets))
|
||||
for _, new := range targets {
|
||||
newMap[new] = true
|
||||
}
|
||||
for _, entity := range entitys {
|
||||
env := entity.(*model.Env)
|
||||
for _, existTarget := range env.Targets {
|
||||
if ok := newMap[existTarget]; ok && env.Name != envName {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func convertEnvModel2Base(env *model.Env, targets []*model.Target) *apisv1.Env {
|
||||
data := apisv1.Env{
|
||||
Name: env.Name,
|
||||
Alias: env.Alias,
|
||||
Description: env.Description,
|
||||
Project: apisv1.NameAlias{Name: env.Project},
|
||||
Namespace: env.Namespace,
|
||||
CreateTime: env.CreateTime,
|
||||
UpdateTime: env.UpdateTime,
|
||||
}
|
||||
for _, dt := range env.Targets {
|
||||
for _, tg := range targets {
|
||||
if dt == tg.Name {
|
||||
data.Targets = append(data.Targets, apisv1.NameAlias{
|
||||
Name: dt,
|
||||
Alias: tg.Alias,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return &data
|
||||
}
|
||||
101
pkg/apiserver/rest/usecase/env_model.go
Normal file
101
pkg/apiserver/rest/usecase/env_model.go
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
util "github.com/oam-dev/kubevela/pkg/utils"
|
||||
velaerr "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
)
|
||||
|
||||
func createEnv(ctx context.Context, kubeClient client.Client, ds datastore.DataStore, env *model.Env) error {
|
||||
tenv := &model.Env{}
|
||||
tenv.Name = env.Name
|
||||
|
||||
exist, err := ds.IsExist(ctx, tenv)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("check if env name exists failure %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if exist {
|
||||
return bcode.ErrEnvAlreadyExists
|
||||
}
|
||||
if env.Namespace == "" {
|
||||
env.Namespace = env.Name
|
||||
}
|
||||
|
||||
// create namespace at first
|
||||
err = util.CreateOrUpdateNamespace(ctx, kubeClient, env.Namespace,
|
||||
util.MergeOverrideLabels(map[string]string{
|
||||
oam.LabelControlPlaneNamespaceUsage: oam.VelaNamespaceUsageEnv,
|
||||
}), util.MergeNoConflictLabels(map[string]string{
|
||||
oam.LabelNamespaceOfEnvName: env.Name,
|
||||
}))
|
||||
if err != nil {
|
||||
if velaerr.IsLabelConflict(err) {
|
||||
return bcode.ErrEnvNamespaceAlreadyBound
|
||||
}
|
||||
log.Logger.Errorf("update namespace label failure %s", err.Error())
|
||||
return bcode.ErrEnvNamespaceFail
|
||||
}
|
||||
if err = ds.Add(ctx, env); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEnv(ctx context.Context, ds datastore.DataStore, envName string) (*model.Env, error) {
|
||||
env := &model.Env{}
|
||||
env.Name = envName
|
||||
if err := ds.Get(ctx, env); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrEnvNotExisted
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func listEnvs(ctx context.Context, ds datastore.DataStore, project string, listOption *datastore.ListOptions) ([]*model.Env, error) {
|
||||
var env = model.Env{}
|
||||
if project != "" {
|
||||
env.Project = project
|
||||
}
|
||||
entities, err := ds.List(ctx, &env, listOption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var envs []*model.Env
|
||||
for _, entity := range entities {
|
||||
apienv, ok := entity.(*model.Env)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
envs = append(envs, apienv)
|
||||
}
|
||||
return envs, nil
|
||||
}
|
||||
122
pkg/apiserver/rest/usecase/env_test.go
Normal file
122
pkg/apiserver/rest/usecase/env_test.go
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
var _ = Describe("Test env usecase functions", func() {
|
||||
var (
|
||||
envUsecase *envUsecaseImpl
|
||||
ds datastore.DataStore
|
||||
)
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "env-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
envUsecase = &envUsecaseImpl{kubeClient: k8sClient, ds: ds}
|
||||
})
|
||||
It("Test Create/Get/Delete Env function", func() {
|
||||
// create target
|
||||
err := ds.Add(context.TODO(), &model.Target{Name: "env-test"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
req := apisv1.CreateEnvRequest{
|
||||
Name: "test-env",
|
||||
Description: "this is a env description",
|
||||
}
|
||||
base, err := envUsecase.CreateEnv(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(base.Namespace, req.Name)).Should(BeEmpty())
|
||||
|
||||
By("test specified namespace to create env")
|
||||
req2 := apisv1.CreateEnvRequest{
|
||||
Name: "test-env-2",
|
||||
Description: "this is a env description",
|
||||
Namespace: base.Namespace,
|
||||
}
|
||||
_, err = envUsecase.CreateEnv(context.TODO(), req2)
|
||||
equal := cmp.Equal(err, bcode.ErrEnvNamespaceAlreadyBound, cmpopts.EquateErrors())
|
||||
Expect(equal).Should(BeTrue())
|
||||
|
||||
req3 := apisv1.CreateEnvRequest{
|
||||
Name: "test-env-2",
|
||||
Description: "this is a env description",
|
||||
Namespace: "default",
|
||||
Project: "env-project",
|
||||
Targets: []string{"env-test"},
|
||||
}
|
||||
base, err = envUsecase.CreateEnv(context.TODO(), req3)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Namespace, "default")).Should(BeEmpty())
|
||||
var namespace corev1.Namespace
|
||||
err = k8sClient.Get(context.TODO(), types.NamespacedName{Name: base.Namespace}, &namespace)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(namespace.Labels[oam.LabelNamespaceOfEnvName], req3.Name)).Should(BeEmpty())
|
||||
|
||||
// test env target conflict
|
||||
req4 := apisv1.CreateEnvRequest{
|
||||
Name: "test-env-3",
|
||||
Description: "this is a env description",
|
||||
Namespace: "default",
|
||||
Project: "env-project",
|
||||
Targets: []string{"env-test"},
|
||||
}
|
||||
_, err = envUsecase.CreateEnv(context.TODO(), req4)
|
||||
Expect(cmp.Equal(err, bcode.ErrEnvTargetConflict, cmpopts.EquateErrors())).Should(BeTrue())
|
||||
|
||||
// test update env
|
||||
req5 := apisv1.UpdateEnvRequest{
|
||||
Description: "this is a env description update",
|
||||
Targets: []string{"env-test"},
|
||||
}
|
||||
env, err := envUsecase.UpdateEnv(context.TODO(), "test-env-2", req5)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(env.Description, req5.Description)).Should(BeEmpty())
|
||||
|
||||
// clean up the env
|
||||
err = envUsecase.DeleteEnv(context.TODO(), "test-env")
|
||||
Expect(err).Should(BeNil())
|
||||
err = envUsecase.DeleteEnv(context.TODO(), "test-env-2")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
By("Test ListEnvs function")
|
||||
_, err = envUsecase.ListEnvs(context.TODO(), 1, 1, apisv1.ListEnvOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("test checkEqual", func() {
|
||||
Expect(checkEqual([]string{"default"}, []string{"default", "dev"})).Should(BeFalse())
|
||||
Expect(checkEqual([]string{"default"}, []string{"default"})).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
@@ -33,8 +33,6 @@ import (
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
utils2 "github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -52,26 +50,25 @@ const (
|
||||
type EnvBindingUsecase interface {
|
||||
GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error)
|
||||
GetEnvBinding(ctx context.Context, app *model.Application, envName string) (*model.EnvBinding, error)
|
||||
CheckAppEnvBindingsContainTarget(ctx context.Context, app *model.Application, targetName string) (bool, error)
|
||||
CreateEnvBinding(ctx context.Context, app *model.Application, env apisv1.CreateApplicationEnvRequest) (*apisv1.EnvBinding, error)
|
||||
CreateEnvBinding(ctx context.Context, app *model.Application, env apisv1.CreateApplicationEnvbindingRequest) (*apisv1.EnvBinding, error)
|
||||
BatchCreateEnvBinding(ctx context.Context, app *model.Application, env apisv1.EnvBindingList) error
|
||||
UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, diff apisv1.PutApplicationEnvRequest) (*apisv1.DetailEnvBindingResponse, error)
|
||||
UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, diff apisv1.PutApplicationEnvBindingRequest) (*apisv1.DetailEnvBindingResponse, error)
|
||||
DeleteEnvBinding(ctx context.Context, app *model.Application, envName string) error
|
||||
BatchDeleteEnvBinding(ctx context.Context, app *model.Application) error
|
||||
DetailEnvBinding(ctx context.Context, app *model.Application, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error)
|
||||
ApplicationEnvRecycle(ctx context.Context, appModel *model.Application, envBinding *model.EnvBinding) error
|
||||
GetSuitableType(ctx context.Context, app *model.Application) string
|
||||
}
|
||||
|
||||
type envBindingUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
workflowUsecase WorkflowUsecase
|
||||
envUsecase EnvUsecase
|
||||
definitionUsecase DefinitionUsecase
|
||||
kubeClient client.Client
|
||||
}
|
||||
|
||||
// NewEnvBindingUsecase new envBinding usecase
|
||||
func NewEnvBindingUsecase(ds datastore.DataStore, workflowUsecase WorkflowUsecase, definitionUsecase DefinitionUsecase) EnvBindingUsecase {
|
||||
func NewEnvBindingUsecase(ds datastore.DataStore, workflowUsecase WorkflowUsecase, definitionUsecase DefinitionUsecase, envUsecase EnvUsecase) EnvBindingUsecase {
|
||||
kubecli, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get kubeclient failure %s", err.Error())
|
||||
@@ -81,32 +78,55 @@ func NewEnvBindingUsecase(ds datastore.DataStore, workflowUsecase WorkflowUsecas
|
||||
workflowUsecase: workflowUsecase,
|
||||
definitionUsecase: definitionUsecase,
|
||||
kubeClient: kubecli,
|
||||
envUsecase: envUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error) {
|
||||
var envBinding = model.EnvBinding{
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
func pickEnv(envs []*model.Env, name string) (*model.Env, error) {
|
||||
for _, e := range envs {
|
||||
if e.Name == name {
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
envBindings, err := e.ds.List(ctx, &envBinding, &datastore.ListOptions{})
|
||||
return nil, bcode.ErrEnvNotExisted
|
||||
}
|
||||
|
||||
func listFullEnvBinding(ctx context.Context, ds datastore.DataStore, option envListOption) ([]*apisv1.EnvBindingBase, error) {
|
||||
envBindings, err := listEnvBindings(ctx, ds, option)
|
||||
if err != nil {
|
||||
|
||||
return nil, bcode.ErrEnvBindingsNotExist
|
||||
}
|
||||
deliveryTarget := model.DeliveryTarget{
|
||||
Namespace: app.Namespace,
|
||||
targets, err := listTarget(ctx, ds, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deliveryTargets, err := e.ds.List(ctx, &deliveryTarget, &datastore.ListOptions{})
|
||||
// TODO: list by project
|
||||
envs, err := listEnvs(ctx, ds, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []*apisv1.EnvBindingBase
|
||||
for _, ebd := range envBindings {
|
||||
eb := ebd.(*model.EnvBinding)
|
||||
list = append(list, convertEnvbindingModelToBase(app, eb, deliveryTargets))
|
||||
for _, eb := range envBindings {
|
||||
env, err := pickEnv(envs, eb.Name)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("envbinding invalid %s", err.Error())
|
||||
continue
|
||||
}
|
||||
list = append(list, convertEnvbindingModelToBase(eb, env, targets))
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error) {
|
||||
full, err := listFullEnvBinding(ctx, e.ds, envListOption{appPrimaryKey: app.PrimaryKey()})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list envbinding for app %s err: %v\n", app.Name, err)
|
||||
return nil, err
|
||||
}
|
||||
return full, nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) GetEnvBinding(ctx context.Context, app *model.Application, envName string) (*model.EnvBinding, error) {
|
||||
envBinding, err := e.getBindingByEnv(ctx, app, envName)
|
||||
if err != nil {
|
||||
@@ -118,11 +138,8 @@ func (e *envBindingUsecaseImpl) GetEnvBinding(ctx context.Context, app *model.Ap
|
||||
return envBinding, nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) CheckAppEnvBindingsContainTarget(ctx context.Context, app *model.Application, targetName string) (bool, error) {
|
||||
envBindings, err := e.GetEnvBindings(ctx, app)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// CheckAppEnvBindingsContainTarget check envbinding contain target
|
||||
func CheckAppEnvBindingsContainTarget(envBindings []*apisv1.EnvBindingBase, targetName string) (bool, error) {
|
||||
var filteredList []*apisv1.EnvBindingBase
|
||||
for _, envBinding := range envBindings {
|
||||
if utils.StringsContain(envBinding.TargetNames, targetName) {
|
||||
@@ -132,7 +149,7 @@ func (e *envBindingUsecaseImpl) CheckAppEnvBindingsContainTarget(ctx context.Con
|
||||
return len(filteredList) > 0, nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) CreateEnvBinding(ctx context.Context, app *model.Application, envReq apisv1.CreateApplicationEnvRequest) (*apisv1.EnvBinding, error) {
|
||||
func (e *envBindingUsecaseImpl) CreateEnvBinding(ctx context.Context, app *model.Application, envReq apisv1.CreateApplicationEnvbindingRequest) (*apisv1.EnvBinding, error) {
|
||||
envBinding, err := e.getBindingByEnv(ctx, app, envReq.Name)
|
||||
if err != nil {
|
||||
if !errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
@@ -142,26 +159,38 @@ func (e *envBindingUsecaseImpl) CreateEnvBinding(ctx context.Context, app *model
|
||||
if envBinding != nil {
|
||||
return nil, bcode.ErrEnvBindingExist
|
||||
}
|
||||
envBindingModel := convertCreateReqToEnvBindingModel(app, envReq)
|
||||
if err := e.ds.Add(ctx, &envBindingModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = e.createEnvWorkflow(ctx, app, &envBindingModel, false)
|
||||
env, err := getEnv(ctx, e.ds, envReq.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envBindingModel := convertCreateReqToEnvBindingModel(app, envReq)
|
||||
err = e.createEnvWorkflow(ctx, app, env, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := e.ds.Add(ctx, &envBindingModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &envReq.EnvBinding, nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) BatchCreateEnvBinding(ctx context.Context, app *model.Application, envbindings apisv1.EnvBindingList) error {
|
||||
for i := range envbindings {
|
||||
envBindingModel := convertToEnvBindingModel(app, *envbindings[i])
|
||||
if err := e.ds.Add(ctx, envBindingModel); err != nil {
|
||||
return err
|
||||
}
|
||||
err := e.createEnvWorkflow(ctx, app, envBindingModel, i == 0)
|
||||
env, err := getEnv(ctx, e.ds, envBindingModel.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Logger.Errorf("get env failure %s", err.Error())
|
||||
continue
|
||||
}
|
||||
if err := e.ds.Add(ctx, envBindingModel); err != nil {
|
||||
log.Logger.Errorf("add envbinding %s failure %s", envBindingModel.Name, err.Error())
|
||||
continue
|
||||
}
|
||||
err = e.createEnvWorkflow(ctx, app, env, i == 0)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("create env workflow failure %s", err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -179,7 +208,7 @@ func (e *envBindingUsecaseImpl) getBindingByEnv(ctx context.Context, app *model.
|
||||
return &envBinding, nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, envUpdate apisv1.PutApplicationEnvRequest) (*apisv1.DetailEnvBindingResponse, error) {
|
||||
func (e *envBindingUsecaseImpl) UpdateEnvBinding(ctx context.Context, app *model.Application, envName string, _ apisv1.PutApplicationEnvBindingRequest) (*apisv1.DetailEnvBindingResponse, error) {
|
||||
envBinding, err := e.getBindingByEnv(ctx, app, envName)
|
||||
if err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
@@ -187,13 +216,16 @@ func (e *envBindingUsecaseImpl) UpdateEnvBinding(ctx context.Context, app *model
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
convertUpdateReqToEnvBindingModel(envBinding, envUpdate)
|
||||
env, err := getEnv(ctx, e.ds, envName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// update env
|
||||
if err := e.ds.Put(ctx, envBinding); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// update env workflow
|
||||
if err := e.updateEnvWorkflow(ctx, app, envBinding); err != nil {
|
||||
if err := UpdateEnvWorkflow(ctx, e.kubeClient, e.ds, app, env); err != nil {
|
||||
return nil, bcode.ErrEnvBindingUpdateWorkflow
|
||||
}
|
||||
return e.DetailEnvBinding(ctx, app, envBinding)
|
||||
@@ -207,14 +239,20 @@ func (e *envBindingUsecaseImpl) DeleteEnvBinding(ctx context.Context, appModel *
|
||||
}
|
||||
return err
|
||||
}
|
||||
var app v1beta1.Application
|
||||
err = e.kubeClient.Get(ctx, types.NamespacedName{Namespace: appModel.Namespace, Name: convertAppName(appModel.Name, envBinding.Name)}, &app)
|
||||
if err == nil || !apierrors.IsNotFound(err) {
|
||||
return bcode.ErrApplicationEnvRefusedDelete
|
||||
}
|
||||
if err := e.ds.Delete(ctx, &model.EnvBinding{AppPrimaryKey: appModel.PrimaryKey(), Name: envBinding.Name}); err != nil {
|
||||
env, err := getEnv(ctx, e.ds, envName)
|
||||
if err != nil && errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return err
|
||||
}
|
||||
if env != nil {
|
||||
var app v1beta1.Application
|
||||
err = e.kubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appModel.Name}, &app)
|
||||
if err == nil || !apierrors.IsNotFound(err) {
|
||||
return bcode.ErrApplicationEnvRefusedDelete
|
||||
}
|
||||
if err := e.ds.Delete(ctx, &model.EnvBinding{AppPrimaryKey: appModel.PrimaryKey(), Name: envBinding.Name}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// delete env workflow
|
||||
if err := e.deleteEnvWorkflow(ctx, appModel, convertWorkflowName(envBinding.Name)); err != nil {
|
||||
return err
|
||||
@@ -241,8 +279,8 @@ func (e *envBindingUsecaseImpl) BatchDeleteEnvBinding(ctx context.Context, app *
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) createEnvWorkflow(ctx context.Context, app *model.Application, env *model.EnvBinding, isDefault bool) error {
|
||||
steps := e.genEnvWorkflowSteps(ctx, env, app)
|
||||
func (e *envBindingUsecaseImpl) createEnvWorkflow(ctx context.Context, app *model.Application, env *model.Env, isDefault bool) error {
|
||||
steps := GenEnvWorkflowSteps(ctx, e.kubeClient, e.ds, env, app)
|
||||
_, err := e.workflowUsecase.CreateOrUpdateWorkflow(ctx, app, apisv1.CreateWorkflowRequest{
|
||||
Name: convertWorkflowName(env.Name),
|
||||
Alias: fmt.Sprintf("%s Workflow", env.Alias),
|
||||
@@ -257,48 +295,6 @@ func (e *envBindingUsecaseImpl) createEnvWorkflow(ctx context.Context, app *mode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) updateEnvWorkflow(ctx context.Context, app *model.Application, env *model.EnvBinding) error {
|
||||
// The existing step configuration should be maintained and the delivery target steps should be automatically updated.
|
||||
envSteps := e.genEnvWorkflowSteps(ctx, env, app)
|
||||
workflow, err := e.workflowUsecase.GetWorkflow(ctx, app, convertWorkflowName(env.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var envStepNames = env.TargetNames
|
||||
var workflowStepNames []string
|
||||
for _, step := range workflow.Steps {
|
||||
if isEnvStepType(step.Type) {
|
||||
workflowStepNames = append(workflowStepNames, step.Name)
|
||||
}
|
||||
}
|
||||
|
||||
var filteredSteps []apisv1.WorkflowStep
|
||||
_, readyToDeleteSteps, readyToAddSteps := compareSlices(workflowStepNames, envStepNames)
|
||||
|
||||
for _, step := range workflow.Steps {
|
||||
if isEnvStepType(step.Type) && utils.StringsContain(readyToDeleteSteps, step.Name) {
|
||||
continue
|
||||
}
|
||||
filteredSteps = append(filteredSteps, convertFromWorkflowStepModel(step))
|
||||
}
|
||||
|
||||
for _, step := range envSteps {
|
||||
if isEnvStepType(step.Type) && utils.StringsContain(readyToAddSteps, step.Name) {
|
||||
filteredSteps = append(filteredSteps, step)
|
||||
}
|
||||
}
|
||||
|
||||
_, err = e.workflowUsecase.UpdateWorkflow(ctx, workflow, apisv1.UpdateWorkflowRequest{
|
||||
Steps: filteredSteps,
|
||||
Description: workflow.Description,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) deleteEnvWorkflow(ctx context.Context, app *model.Application, workflowName string) error {
|
||||
if err := e.workflowUsecase.DeleteWorkflow(ctx, app, workflowName); err != nil {
|
||||
if !errors.Is(err, bcode.ErrWorkflowNotExist) {
|
||||
@@ -309,21 +305,26 @@ func (e *envBindingUsecaseImpl) deleteEnvWorkflow(ctx context.Context, app *mode
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) DetailEnvBinding(ctx context.Context, app *model.Application, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) {
|
||||
deliveryTarget := model.DeliveryTarget{
|
||||
Namespace: app.Namespace,
|
||||
targets, err := listTarget(ctx, e.ds, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deliveryTargets, err := e.ds.List(ctx, &deliveryTarget, &datastore.ListOptions{})
|
||||
env, err := getEnv(ctx, e.ds, envBinding.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apisv1.DetailEnvBindingResponse{
|
||||
EnvBindingBase: *convertEnvbindingModelToBase(app, envBinding, deliveryTargets),
|
||||
EnvBindingBase: *convertEnvbindingModelToBase(envBinding, env, targets),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) ApplicationEnvRecycle(ctx context.Context, appModel *model.Application, envBinding *model.EnvBinding) error {
|
||||
env, err := getEnv(ctx, e.ds, envBinding.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var app v1beta1.Application
|
||||
err := e.kubeClient.Get(ctx, types.NamespacedName{Namespace: appModel.Namespace, Name: convertAppName(appModel.Name, envBinding.Name)}, &app)
|
||||
err = e.kubeClient.Get(ctx, types.NamespacedName{Namespace: env.Namespace, Name: appModel.Name}, &app)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
@@ -340,25 +341,21 @@ func (e *envBindingUsecaseImpl) ApplicationEnvRecycle(ctx context.Context, appMo
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertCreateReqToEnvBindingModel(app *model.Application, req apisv1.CreateApplicationEnvRequest) model.EnvBinding {
|
||||
func convertCreateReqToEnvBindingModel(app *model.Application, req apisv1.CreateApplicationEnvbindingRequest) model.EnvBinding {
|
||||
envBinding := model.EnvBinding{
|
||||
AppPrimaryKey: app.Name,
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
TargetNames: req.TargetNames,
|
||||
}
|
||||
return envBinding
|
||||
}
|
||||
|
||||
func convertEnvbindingModelToBase(app *model.Application, envBinding *model.EnvBinding, deliveryTargets []datastore.Entity) *apisv1.EnvBindingBase {
|
||||
var dtMap = make(map[string]*model.DeliveryTarget, len(deliveryTargets))
|
||||
for _, dte := range deliveryTargets {
|
||||
dt := dte.(*model.DeliveryTarget)
|
||||
dtMap[dt.Name] = dt
|
||||
func convertEnvbindingModelToBase(envBinding *model.EnvBinding, env *model.Env, targets []*model.Target) *apisv1.EnvBindingBase {
|
||||
var dtMap = make(map[string]*model.Target, len(targets))
|
||||
for _, dte := range targets {
|
||||
dtMap[dte.Name] = dte
|
||||
}
|
||||
var targets []apisv1.EnvBindingTarget
|
||||
for _, targetName := range envBinding.TargetNames {
|
||||
var envBindingTargets []apisv1.EnvBindingTarget
|
||||
for _, targetName := range env.Targets {
|
||||
dt := dtMap[targetName]
|
||||
if dt != nil {
|
||||
ebt := apisv1.EnvBindingTarget{
|
||||
@@ -370,108 +367,31 @@ func convertEnvbindingModelToBase(app *model.Application, envBinding *model.EnvB
|
||||
Namespace: dt.Cluster.Namespace,
|
||||
}
|
||||
}
|
||||
targets = append(targets, ebt)
|
||||
envBindingTargets = append(envBindingTargets, ebt)
|
||||
}
|
||||
}
|
||||
ebb := &apisv1.EnvBindingBase{
|
||||
Name: envBinding.Name,
|
||||
Alias: envBinding.Alias,
|
||||
Description: envBinding.Description,
|
||||
TargetNames: envBinding.TargetNames,
|
||||
Targets: targets,
|
||||
ComponentSelector: (*apisv1.ComponentSelector)(envBinding.ComponentSelector),
|
||||
CreateTime: envBinding.CreateTime,
|
||||
UpdateTime: envBinding.UpdateTime,
|
||||
AppDeployName: convertAppName(app.Name, envBinding.Name),
|
||||
Name: envBinding.Name,
|
||||
Alias: env.Alias,
|
||||
Description: env.Description,
|
||||
TargetNames: env.Targets,
|
||||
Targets: envBindingTargets,
|
||||
CreateTime: envBinding.CreateTime,
|
||||
UpdateTime: envBinding.UpdateTime,
|
||||
AppDeployName: envBinding.AppPrimaryKey,
|
||||
AppDeployNamespace: env.Namespace,
|
||||
}
|
||||
return ebb
|
||||
}
|
||||
|
||||
func convertUpdateReqToEnvBindingModel(envBinding *model.EnvBinding, envUpdate apisv1.PutApplicationEnvRequest) *model.EnvBinding {
|
||||
envBinding.Alias = envUpdate.Alias
|
||||
envBinding.Description = envUpdate.Description
|
||||
envBinding.TargetNames = envUpdate.TargetNames
|
||||
if envUpdate.ComponentSelector != nil {
|
||||
envBinding.ComponentSelector = (*model.ComponentSelector)(envUpdate.ComponentSelector)
|
||||
}
|
||||
return envBinding
|
||||
}
|
||||
|
||||
func convertToEnvBindingModel(app *model.Application, envBind apisv1.EnvBinding) *model.EnvBinding {
|
||||
re := model.EnvBinding{
|
||||
AppPrimaryKey: app.Name,
|
||||
Name: envBind.Name,
|
||||
Description: envBind.Description,
|
||||
Alias: envBind.Alias,
|
||||
TargetNames: envBind.TargetNames,
|
||||
}
|
||||
if envBind.ComponentSelector != nil {
|
||||
re.ComponentSelector = (*model.ComponentSelector)(envBind.ComponentSelector)
|
||||
}
|
||||
return &re
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) GetSuitableType(ctx context.Context, app *model.Application) string {
|
||||
components, err := e.ds.List(ctx, &model.ApplicationComponent{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{PageSize: 1, Page: 1})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list application component list failure %s", err.Error())
|
||||
}
|
||||
if len(components) > 0 {
|
||||
component := components[0].(*model.ApplicationComponent)
|
||||
definition, err := e.definitionUsecase.GetComponentDefinition(ctx, component.Type)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("get component definition %s failure %s", component.Type, err.Error())
|
||||
}
|
||||
if definition != nil {
|
||||
if definition.Spec.Workload.Type == TerraformWorkfloadType {
|
||||
return DeployCloudResource
|
||||
}
|
||||
if definition.Spec.Workload.Definition.Kind == TerraformWorkfloadKind {
|
||||
return DeployCloudResource
|
||||
}
|
||||
}
|
||||
}
|
||||
return Deploy2Env
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) genEnvWorkflowSteps(ctx context.Context, env *model.EnvBinding, app *model.Application) []apisv1.WorkflowStep {
|
||||
var workflowSteps []v1beta1.WorkflowStep
|
||||
for _, targetName := range env.TargetNames {
|
||||
step := v1beta1.WorkflowStep{
|
||||
Name: genPolicyEnvName(targetName),
|
||||
Type: e.GetSuitableType(ctx, app),
|
||||
Properties: util.Object2RawExtension(map[string]string{
|
||||
"policy": genPolicyName(env.Name),
|
||||
"env": genPolicyEnvName(targetName),
|
||||
}),
|
||||
}
|
||||
workflowSteps = append(workflowSteps, step)
|
||||
}
|
||||
var steps []apisv1.WorkflowStep
|
||||
for _, step := range workflowSteps {
|
||||
var propertyStr string
|
||||
if step.Properties != nil {
|
||||
properties, err := model.NewJSONStruct(step.Properties)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("workflow %s step %s properties is invalid %s", utils2.Sanitize(app.Name), utils2.Sanitize(step.Name), err.Error())
|
||||
continue
|
||||
}
|
||||
propertyStr = properties.JSON()
|
||||
}
|
||||
steps = append(steps, apisv1.WorkflowStep{
|
||||
Name: step.Name,
|
||||
Type: step.Type,
|
||||
Alias: fmt.Sprintf("Deploy To %s", step.Name),
|
||||
Description: fmt.Sprintf("deploy app to delivery target %s", step.Name),
|
||||
DependsOn: step.DependsOn,
|
||||
Properties: propertyStr,
|
||||
Inputs: step.Inputs,
|
||||
Outputs: step.Outputs,
|
||||
})
|
||||
}
|
||||
return steps
|
||||
}
|
||||
|
||||
func convertWorkflowName(envName string) string {
|
||||
return fmt.Sprintf("workflow-%s", envName)
|
||||
}
|
||||
|
||||
52
pkg/apiserver/rest/usecase/envbinding_model.go
Normal file
52
pkg/apiserver/rest/usecase/envbinding_model.go
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
)
|
||||
|
||||
type envListOption struct {
|
||||
appPrimaryKey string
|
||||
envName string
|
||||
}
|
||||
|
||||
func listEnvBindings(ctx context.Context, ds datastore.DataStore, listOption envListOption) ([]*model.EnvBinding, error) {
|
||||
var envBinding = model.EnvBinding{}
|
||||
if listOption.appPrimaryKey != "" {
|
||||
envBinding.AppPrimaryKey = listOption.appPrimaryKey
|
||||
}
|
||||
if listOption.envName != "" {
|
||||
envBinding.Name = listOption.envName
|
||||
}
|
||||
envBindings, err := ds.List(ctx, &envBinding, &datastore.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ret []*model.EnvBinding
|
||||
for _, et := range envBindings {
|
||||
eb, ok := et.(*model.EnvBinding)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, eb)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
@@ -23,59 +23,74 @@ import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
)
|
||||
|
||||
var _ = Describe("Test envBindingUsecase functions", func() {
|
||||
var (
|
||||
envUsecase *envUsecaseImpl
|
||||
envBindingUsecase *envBindingUsecaseImpl
|
||||
workflowUsecase *workflowUsecaseImpl
|
||||
definitionUsecase DefinitionUsecase
|
||||
envBindingDemo1 apisv1.EnvBinding
|
||||
envBindingDemo2 apisv1.EnvBinding
|
||||
testApp *model.Application
|
||||
ds datastore.DataStore
|
||||
)
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "env-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
testApp = &model.Application{
|
||||
Name: "test-app-env",
|
||||
Namespace: "default",
|
||||
Name: "test-app-env",
|
||||
}
|
||||
workflowUsecase = &workflowUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
workflowUsecase = &workflowUsecaseImpl{ds: ds, kubeClient: k8sClient, envUsecase: envUsecase}
|
||||
definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient, caches: make(map[string]*utils.MemoryCache)}
|
||||
envBindingUsecase = &envBindingUsecaseImpl{ds: ds, workflowUsecase: workflowUsecase, definitionUsecase: definitionUsecase, kubeClient: k8sClient}
|
||||
envBindingUsecase = &envBindingUsecaseImpl{ds: ds, workflowUsecase: workflowUsecase, definitionUsecase: definitionUsecase, kubeClient: k8sClient, envUsecase: envUsecase}
|
||||
envBindingDemo1 = apisv1.EnvBinding{
|
||||
Name: "dev",
|
||||
Alias: "dev alias",
|
||||
TargetNames: []string{"dev-target"},
|
||||
Name: "envbinding-dev",
|
||||
}
|
||||
envBindingDemo2 = apisv1.EnvBinding{
|
||||
Name: "prod",
|
||||
Alias: "prod alias",
|
||||
TargetNames: []string{"prod-target"},
|
||||
Name: "envbinding-prod",
|
||||
}
|
||||
})
|
||||
|
||||
It("Test Create Application Env function", func() {
|
||||
|
||||
// create target
|
||||
err := ds.Add(context.TODO(), &model.Target{Name: "dev-target"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = ds.Add(context.TODO(), &model.Target{Name: "prod-target"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "envbinding-dev", Targets: []string{"dev-target"}})
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "envbinding-prod", Targets: []string{"prod-target"}})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
By("create two envbinding")
|
||||
req := apisv1.CreateApplicationEnvRequest{EnvBinding: envBindingDemo1}
|
||||
req := apisv1.CreateApplicationEnvbindingRequest{EnvBinding: envBindingDemo1}
|
||||
base, err := envBindingUsecase.CreateEnvBinding(context.TODO(), testApp, req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
|
||||
|
||||
req = apisv1.CreateApplicationEnvRequest{EnvBinding: envBindingDemo2}
|
||||
req = apisv1.CreateApplicationEnvbindingRequest{EnvBinding: envBindingDemo2}
|
||||
base, err = envBindingUsecase.CreateEnvBinding(context.TODO(), testApp, req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
|
||||
|
||||
By("auto create two workflow")
|
||||
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), testApp, "workflow-dev")
|
||||
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), testApp, convertWorkflowName("envbinding-dev"))
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(workflow.Steps[0].Name, "dev-target")).Should(BeEmpty())
|
||||
|
||||
workflow, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, "workflow-prod")
|
||||
workflow, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, convertWorkflowName("envbinding-prod"))
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(workflow.Steps[0].Name, "prod-target")).Should(BeEmpty())
|
||||
})
|
||||
@@ -88,56 +103,37 @@ var _ = Describe("Test envBindingUsecase functions", func() {
|
||||
})
|
||||
|
||||
It("Test GetApplication Env function", func() {
|
||||
envBinding, err := envBindingUsecase.GetEnvBinding(context.TODO(), testApp, "dev")
|
||||
envBinding, err := envBindingUsecase.GetEnvBinding(context.TODO(), testApp, "envbinding-dev")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(envBinding).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff(envBinding.Name, "dev")).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test CheckAppEnvBindingsContainTarget function", func() {
|
||||
isContain, err := envBindingUsecase.CheckAppEnvBindingsContainTarget(context.TODO(), testApp, "dev-target")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(isContain).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff(isContain, true)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(envBinding.Name, "envbinding-dev")).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test Application UpdateEnv function", func() {
|
||||
envBinding, err := envBindingUsecase.UpdateEnvBinding(context.TODO(), testApp, "prod", apisv1.PutApplicationEnvRequest{
|
||||
TargetNames: []string{"prod-target-new1", "prod-target-new2"},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(envBinding).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff(envBinding.TargetNames[0], "prod-target-new1")).Should(BeEmpty())
|
||||
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), testApp, "workflow-prod")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(workflow.Steps[0].Name, "prod-target-new1")).Should(BeEmpty())
|
||||
|
||||
envBinding, err = envBindingUsecase.UpdateEnvBinding(context.TODO(), testApp, "prod", apisv1.PutApplicationEnvRequest{
|
||||
TargetNames: []string{"prod-target-new3", "prod-target-new2"},
|
||||
})
|
||||
envBinding, err := envBindingUsecase.UpdateEnvBinding(context.TODO(), testApp, "envbinding-prod", apisv1.PutApplicationEnvBindingRequest{})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(envBinding).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff(envBinding.TargetNames[0], "prod-target-new3")).Should(BeEmpty())
|
||||
workflow, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, "workflow-prod")
|
||||
Expect(cmp.Diff(envBinding.TargetNames[0], "prod-target")).Should(BeEmpty())
|
||||
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), testApp, "workflow-envbinding-prod")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(workflow.Steps[1].Name, "prod-target-new3")).Should(BeEmpty())
|
||||
Expect(cmp.Diff(workflow.Steps[0].Name, "prod-target")).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test Application DeleteEnv function", func() {
|
||||
err := envBindingUsecase.DeleteEnvBinding(context.TODO(), testApp, "dev")
|
||||
err := envBindingUsecase.DeleteEnvBinding(context.TODO(), testApp, "envbinding-dev")
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, "dev")
|
||||
_, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, convertWorkflowName("envbinding-dev"))
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
err = envBindingUsecase.DeleteEnvBinding(context.TODO(), testApp, "prod")
|
||||
err = envBindingUsecase.DeleteEnvBinding(context.TODO(), testApp, "envbinding-prod")
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, "prod")
|
||||
_, err = workflowUsecase.GetWorkflow(context.TODO(), testApp, convertWorkflowName("envbinding-prod"))
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("Test Application BatchCreateEnv function", func() {
|
||||
testBatchApp := &model.Application{
|
||||
Name: "test-batch-createt",
|
||||
Namespace: "default",
|
||||
Name: "test-batch-createt",
|
||||
}
|
||||
err := envBindingUsecase.BatchCreateEnvBinding(context.TODO(), testBatchApp, apisv1.EnvBindingList{&envBindingDemo1, &envBindingDemo2})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -19,12 +19,7 @@ package usecase
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
@@ -33,11 +28,23 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
)
|
||||
|
||||
// ProjectNamespace mark the usage of the namespace.
|
||||
const ProjectNamespace = "project"
|
||||
const (
|
||||
|
||||
// DefaultInitName is default object name for initialization
|
||||
DefaultInitName = "default"
|
||||
// DefaultInitNamespace is default namespace name for initialization
|
||||
DefaultInitNamespace = "default"
|
||||
|
||||
// DefaultTargetDescription describes default target created
|
||||
DefaultTargetDescription = "Default target is created by velaux system automatically."
|
||||
// DefaultEnvDescription describes default env created
|
||||
DefaultEnvDescription = "Default environment is created by velaux system automatically."
|
||||
// DefaultProjectDescription describes the default project created
|
||||
DefaultProjectDescription = "Default project is created by velaux system automatically."
|
||||
)
|
||||
|
||||
// ProjectUsecase project manage usecase.
|
||||
type ProjectUsecase interface {
|
||||
@@ -47,17 +54,80 @@ type ProjectUsecase interface {
|
||||
}
|
||||
|
||||
type projectUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
kubeClient client.Client
|
||||
ds datastore.DataStore
|
||||
k8sClient client.Client
|
||||
}
|
||||
|
||||
// NewProjectUsecase new project usecase
|
||||
func NewProjectUsecase(ds datastore.DataStore) ProjectUsecase {
|
||||
kubecli, err := clients.GetKubeClient()
|
||||
k8sClient, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get kubeclient failure %s", err.Error())
|
||||
log.Logger.Fatalf("get k8sClient failure: %s", err.Error())
|
||||
}
|
||||
p := &projectUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
p.initDefaultProjectEnvTarget(DefaultInitNamespace)
|
||||
return p
|
||||
}
|
||||
|
||||
// initDefaultProjectEnvTarget will initialize a default project with a default env that contain a default target
|
||||
// the default env and default target both using the `default` namespace in control plane cluster
|
||||
func (p *projectUsecaseImpl) initDefaultProjectEnvTarget(defaultNamespace string) {
|
||||
|
||||
ctx := context.Background()
|
||||
entities, err := listProjects(ctx, p.ds)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("initialize project failed %v", err)
|
||||
return
|
||||
}
|
||||
if len(entities) > 0 {
|
||||
return
|
||||
}
|
||||
log.Logger.Info("no default project found, adding a default project with default env and target")
|
||||
|
||||
if err := createTargetNamespace(ctx, p.k8sClient, multicluster.ClusterLocalName, defaultNamespace, DefaultInitName); err != nil {
|
||||
log.Logger.Errorf("initialize default target namespace failed %v", err)
|
||||
return
|
||||
}
|
||||
// initialize default target first
|
||||
err = createTarget(ctx, p.ds, &model.Target{
|
||||
Name: DefaultInitName,
|
||||
Alias: "Default",
|
||||
Description: DefaultTargetDescription,
|
||||
Cluster: &model.ClusterTarget{
|
||||
ClusterName: multicluster.ClusterLocalName,
|
||||
Namespace: defaultNamespace,
|
||||
},
|
||||
})
|
||||
// for idempotence, ignore default target already exist error
|
||||
if err != nil && errors.Is(err, bcode.ErrTargetExist) {
|
||||
log.Logger.Errorf("initialize default target failed %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// initialize default target first
|
||||
err = createEnv(ctx, p.k8sClient, p.ds, &model.Env{
|
||||
Name: DefaultInitName,
|
||||
Alias: "Default",
|
||||
Description: DefaultEnvDescription,
|
||||
Project: DefaultInitName,
|
||||
Namespace: defaultNamespace,
|
||||
Targets: []string{DefaultInitName},
|
||||
})
|
||||
// for idempotence, ignore default env already exist error
|
||||
if err != nil && errors.Is(err, bcode.ErrEnvAlreadyExists) {
|
||||
log.Logger.Errorf("initialize default environment failed %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = p.CreateProject(ctx, apisv1.CreateProjectRequest{
|
||||
Name: DefaultInitName,
|
||||
Alias: "Default",
|
||||
Description: DefaultProjectDescription,
|
||||
})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("initialize project failed %v", err)
|
||||
return
|
||||
}
|
||||
return &projectUsecaseImpl{kubeClient: kubecli, ds: ds}
|
||||
}
|
||||
|
||||
// GetProject get project
|
||||
@@ -72,10 +142,9 @@ func (p *projectUsecaseImpl) GetProject(ctx context.Context, projectName string)
|
||||
return project, nil
|
||||
}
|
||||
|
||||
// ListProjects list projects
|
||||
func (p *projectUsecaseImpl) ListProjects(ctx context.Context) ([]*apisv1.ProjectBase, error) {
|
||||
func listProjects(ctx context.Context, ds datastore.DataStore) ([]*apisv1.ProjectBase, error) {
|
||||
var project = model.Project{}
|
||||
entitys, err := p.ds.List(ctx, &project, &datastore.ListOptions{SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
entitys, err := ds.List(ctx, &project, &datastore.ListOptions{SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -87,6 +156,20 @@ func (p *projectUsecaseImpl) ListProjects(ctx context.Context) ([]*apisv1.Projec
|
||||
return projects, nil
|
||||
}
|
||||
|
||||
// ListProjects list projects
|
||||
func (p *projectUsecaseImpl) ListProjects(ctx context.Context) ([]*apisv1.ProjectBase, error) {
|
||||
return listProjects(ctx, p.ds)
|
||||
}
|
||||
|
||||
// DeleteProject delete a project
|
||||
func (p *projectUsecaseImpl) DeleteProject(ctx context.Context, name string) error {
|
||||
|
||||
// TODO(@wonderflow): it's not supported for delete a project now, just used in test
|
||||
// we should prevent delete a project that contain any application/env inside.
|
||||
|
||||
return p.ds.Delete(ctx, &model.Project{Name: name})
|
||||
}
|
||||
|
||||
// CreateProject create project
|
||||
func (p *projectUsecaseImpl) CreateProject(ctx context.Context, req apisv1.CreateProjectRequest) (*apisv1.ProjectBase, error) {
|
||||
|
||||
@@ -99,68 +182,28 @@ func (p *projectUsecaseImpl) CreateProject(ctx context.Context, req apisv1.Creat
|
||||
return nil, bcode.ErrProjectIsExist
|
||||
}
|
||||
|
||||
new := &model.Project{
|
||||
newProject := &model.Project{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Alias: req.Alias,
|
||||
Namespace: fmt.Sprintf("project-%s", req.Name),
|
||||
}
|
||||
if req.Namespace != "" {
|
||||
new.Namespace = req.Namespace
|
||||
}
|
||||
// create namespace at first
|
||||
if err := p.kubeClient.Create(ctx, &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: new.Namespace,
|
||||
Labels: map[string]string{
|
||||
oam.LabelProjectNamesapce: new.Name,
|
||||
oam.LabelUsageNamespace: ProjectNamespace,
|
||||
},
|
||||
},
|
||||
Spec: corev1.NamespaceSpec{},
|
||||
}); err != nil {
|
||||
if !apierrors.IsAlreadyExists(err) {
|
||||
return nil, err
|
||||
}
|
||||
if apierrors.IsAlreadyExists(err) {
|
||||
var namespace corev1.Namespace
|
||||
if err := p.kubeClient.Get(ctx, k8stypes.NamespacedName{Name: new.Namespace}, &namespace); err != nil {
|
||||
return nil, bcode.ErrProjectNamespaceFail
|
||||
}
|
||||
if _, exist := namespace.Labels[oam.LabelProjectNamesapce]; !exist {
|
||||
if namespace.Labels == nil {
|
||||
namespace.Labels = make(map[string]string)
|
||||
}
|
||||
namespace.Labels[oam.LabelProjectNamesapce] = new.Name
|
||||
namespace.Labels[oam.LabelUsageNamespace] = ProjectNamespace
|
||||
if err := p.kubeClient.Update(ctx, &namespace); err != nil {
|
||||
log.Logger.Errorf("update namespace label failure %s", err.Error())
|
||||
return nil, bcode.ErrProjectNamespaceFail
|
||||
}
|
||||
} else {
|
||||
return nil, bcode.ErrProjectNamespaceIsExist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := p.ds.Add(ctx, new); err != nil {
|
||||
if err := p.ds.Add(ctx, newProject); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &apisv1.ProjectBase{
|
||||
Name: new.Name,
|
||||
Alias: new.Alias,
|
||||
Namespace: new.Namespace,
|
||||
Description: new.Description,
|
||||
CreateTime: new.CreateTime,
|
||||
UpdateTime: new.UpdateTime,
|
||||
Name: newProject.Name,
|
||||
Alias: newProject.Alias,
|
||||
Description: newProject.Description,
|
||||
CreateTime: newProject.CreateTime,
|
||||
UpdateTime: newProject.UpdateTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertProjectModel2Base(project *model.Project) *apisv1.ProjectBase {
|
||||
return &apisv1.ProjectBase{
|
||||
Name: project.Name,
|
||||
Namespace: project.Namespace,
|
||||
Description: project.Description,
|
||||
Alias: project.Alias,
|
||||
CreateTime: project.CreateTime,
|
||||
|
||||
@@ -18,28 +18,110 @@ package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Test project usecase functions", func() {
|
||||
var (
|
||||
projectUsecase *projectUsecaseImpl
|
||||
projectUsecase *projectUsecaseImpl
|
||||
envImpl *envUsecaseImpl
|
||||
targetImpl *targetUsecaseImpl
|
||||
defaultNamespace = "project-default-ns1-test"
|
||||
)
|
||||
BeforeEach(func() {
|
||||
projectUsecase = &projectUsecaseImpl{kubeClient: k8sClient, ds: ds}
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "target-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
var ns = corev1.Namespace{}
|
||||
ns.Name = defaultNamespace
|
||||
err = k8sClient.Create(context.TODO(), &ns)
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
projectUsecase = &projectUsecaseImpl{k8sClient: k8sClient, ds: ds}
|
||||
pp, err := projectUsecase.ListProjects(context.TODO())
|
||||
Expect(err).Should(BeNil())
|
||||
// reset all projects
|
||||
for _, p := range pp {
|
||||
_ = projectUsecase.DeleteProject(context.TODO(), p.Name)
|
||||
}
|
||||
|
||||
envImpl = &envUsecaseImpl{kubeClient: k8sClient, ds: ds}
|
||||
envs, err := envImpl.ListEnvs(context.TODO(), 0, 0, apisv1.ListEnvOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
// reset all projects
|
||||
for _, e := range envs {
|
||||
_ = envImpl.DeleteEnv(context.TODO(), e.Name)
|
||||
}
|
||||
targetImpl = &targetUsecaseImpl{k8sClient: k8sClient, ds: ds}
|
||||
targs, err := targetImpl.ListTargets(context.TODO(), 0, 0)
|
||||
Expect(err).Should(BeNil())
|
||||
// reset all projects
|
||||
for _, e := range targs.Targets {
|
||||
_ = envImpl.DeleteEnv(context.TODO(), e.Name)
|
||||
}
|
||||
})
|
||||
It("Test Createproject function", func() {
|
||||
It("Test project initialize function", func() {
|
||||
|
||||
projectUsecase.initDefaultProjectEnvTarget(defaultNamespace)
|
||||
By("test env created")
|
||||
var namespace corev1.Namespace
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(context.TODO(), types.NamespacedName{Name: defaultNamespace}, &namespace)
|
||||
}, time.Second*3, time.Microsecond*300).Should(BeNil())
|
||||
|
||||
Expect(cmp.Diff(namespace.Labels[oam.LabelNamespaceOfEnvName], DefaultInitName)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(namespace.Labels[oam.LabelNamespaceOfTargetName], DefaultInitName)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(namespace.Labels[oam.LabelControlPlaneNamespaceUsage], oam.VelaNamespaceUsageEnv)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(namespace.Labels[oam.LabelRuntimeNamespaceUsage], oam.VelaNamespaceUsageTarget)).Should(BeEmpty())
|
||||
|
||||
By("check project created")
|
||||
dp, err := projectUsecase.GetProject(context.TODO(), DefaultInitName)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(dp.Alias).Should(BeEquivalentTo("Default"))
|
||||
Expect(dp.Description).Should(BeEquivalentTo(DefaultProjectDescription))
|
||||
|
||||
By("check env created")
|
||||
|
||||
env, err := envImpl.GetEnv(context.TODO(), DefaultInitName)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(env.Alias).Should(BeEquivalentTo("Default"))
|
||||
Expect(env.Description).Should(BeEquivalentTo(DefaultEnvDescription))
|
||||
Expect(env.Project).Should(BeEquivalentTo(DefaultInitName))
|
||||
Expect(env.Targets).Should(BeEquivalentTo([]string{DefaultInitName}))
|
||||
Expect(env.Namespace).Should(BeEquivalentTo(defaultNamespace))
|
||||
|
||||
By("check target created")
|
||||
|
||||
tg, err := targetImpl.GetTarget(context.TODO(), DefaultInitName)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(tg.Alias).Should(BeEquivalentTo("Default"))
|
||||
Expect(tg.Description).Should(BeEquivalentTo(DefaultTargetDescription))
|
||||
Expect(tg.Cluster).Should(BeEquivalentTo(&model.ClusterTarget{
|
||||
ClusterName: multicluster.ClusterLocalName,
|
||||
Namespace: defaultNamespace,
|
||||
}))
|
||||
Expect(env.Targets).Should(BeEquivalentTo([]string{DefaultInitName}))
|
||||
Expect(env.Namespace).Should(BeEquivalentTo(defaultNamespace))
|
||||
|
||||
err = targetImpl.DeleteTarget(context.TODO(), DefaultInitName)
|
||||
Expect(err).Should(BeNil())
|
||||
err = envImpl.DeleteEnv(context.TODO(), DefaultInitName)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
})
|
||||
It("Test Create project function", func() {
|
||||
req := apisv1.CreateProjectRequest{
|
||||
Name: "test-project",
|
||||
Description: "this is a project description",
|
||||
@@ -47,34 +129,9 @@ var _ = Describe("Test project usecase functions", func() {
|
||||
base, err := projectUsecase.CreateProject(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(base.Namespace, fmt.Sprintf("project-%s", req.Name))).Should(BeEmpty())
|
||||
|
||||
By("test specified namespace to create project")
|
||||
req2 := apisv1.CreateProjectRequest{
|
||||
Name: "test-project-2",
|
||||
Description: "this is a project description",
|
||||
Namespace: base.Namespace,
|
||||
}
|
||||
_, err = projectUsecase.CreateProject(context.TODO(), req2)
|
||||
equal := cmp.Equal(err, bcode.ErrProjectNamespaceIsExist, cmpopts.EquateErrors())
|
||||
Expect(equal).Should(BeTrue())
|
||||
|
||||
req3 := apisv1.CreateProjectRequest{
|
||||
Name: "test-project-2",
|
||||
Description: "this is a project description",
|
||||
Namespace: "default",
|
||||
}
|
||||
base, err = projectUsecase.CreateProject(context.TODO(), req3)
|
||||
_, err = projectUsecase.ListProjects(context.TODO())
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Namespace, "default")).Should(BeEmpty())
|
||||
var namespace corev1.Namespace
|
||||
err = k8sClient.Get(context.TODO(), types.NamespacedName{Name: base.Namespace}, &namespace)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(namespace.Labels[oam.LabelProjectNamesapce], req3.Name)).Should(BeEmpty())
|
||||
projectUsecase.DeleteProject(context.TODO(), "test-project")
|
||||
})
|
||||
|
||||
It("Test ListProject function", func() {
|
||||
_, err := projectUsecase.ListProjects(context.TODO())
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -41,7 +41,11 @@ import (
|
||||
var cfg *rest.Config
|
||||
var k8sClient client.Client
|
||||
var testEnv *envtest.Environment
|
||||
var ds datastore.DataStore
|
||||
|
||||
func TestUsecase(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Usecase Suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
@@ -67,9 +71,7 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
By("new kube client success")
|
||||
clients.SetKubeClient(k8sClient)
|
||||
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "kubevela"})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(ds).ToNot(BeNil())
|
||||
close(done)
|
||||
}, 240)
|
||||
|
||||
@@ -97,11 +99,6 @@ func NewDatastore(cfg datastore.Config) (ds datastore.DataStore, err error) {
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func TestUsecase(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Usecase Suite")
|
||||
}
|
||||
|
||||
func randomNamespaceName(basic string) string {
|
||||
return fmt.Sprintf("%s-%s", basic, strconv.FormatInt(rand.Int63(), 16))
|
||||
}
|
||||
192
pkg/apiserver/rest/usecase/target.go
Normal file
192
pkg/apiserver/rest/usecase/target.go
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
)
|
||||
|
||||
// TargetUsecase Target manage api
|
||||
type TargetUsecase interface {
|
||||
GetTarget(ctx context.Context, TargetName string) (*model.Target, error)
|
||||
DetailTarget(ctx context.Context, Target *model.Target) (*apisv1.DetailTargetResponse, error)
|
||||
DeleteTarget(ctx context.Context, TargetName string) error
|
||||
CreateTarget(ctx context.Context, req apisv1.CreateTargetRequest) (*apisv1.DetailTargetResponse, error)
|
||||
UpdateTarget(ctx context.Context, Target *model.Target, req apisv1.UpdateTargetRequest) (*apisv1.DetailTargetResponse, error)
|
||||
ListTargets(ctx context.Context, page, pageSize int) (*apisv1.ListTargetResponse, error)
|
||||
}
|
||||
|
||||
type targetUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
k8sClient client.Client
|
||||
}
|
||||
|
||||
// NewTargetUsecase new Target usecase
|
||||
func NewTargetUsecase(ds datastore.DataStore) TargetUsecase {
|
||||
k8sClient, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get k8sClient failure: %s", err.Error())
|
||||
}
|
||||
return &targetUsecaseImpl{
|
||||
k8sClient: k8sClient,
|
||||
ds: ds,
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *targetUsecaseImpl) ListTargets(ctx context.Context, page, pageSize int) (*apisv1.ListTargetResponse, error) {
|
||||
|
||||
Targets, err := listTarget(ctx, dt.ds, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := &apisv1.ListTargetResponse{
|
||||
Targets: []apisv1.TargetBase{},
|
||||
}
|
||||
for _, raw := range Targets {
|
||||
resp.Targets = append(resp.Targets, *(dt.convertFromTargetModel(ctx, raw)))
|
||||
}
|
||||
count, err := dt.ds.Count(ctx, &model.Target{}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Total = count
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DeleteTarget delete application Target
|
||||
func (dt *targetUsecaseImpl) DeleteTarget(ctx context.Context, targetName string) error {
|
||||
Target := &model.Target{
|
||||
Name: targetName,
|
||||
}
|
||||
ddt, err := dt.GetTarget(ctx, targetName)
|
||||
if errors.Is(err, datastore.ErrRecordExist) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = deleteTargetNamespace(ctx, dt.k8sClient, ddt.Cluster.ClusterName, ddt.Cluster.Namespace, targetName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dt.ds.Delete(ctx, Target); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return bcode.ErrTargetNotExist
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTarget will create a delivery target binding with a cluster and namespace, by default, it will use local cluster and namespace align with targetName
|
||||
// TODO(@wonderflow): we should support empty target in the future which only delivery cloud resources
|
||||
func (dt *targetUsecaseImpl) CreateTarget(ctx context.Context, req apisv1.CreateTargetRequest) (*apisv1.DetailTargetResponse, error) {
|
||||
Target := convertCreateReqToTargetModel(req)
|
||||
if req.Cluster == nil {
|
||||
req.Cluster = &apisv1.ClusterTarget{ClusterName: multicluster.ClusterLocalName, Namespace: req.Name}
|
||||
}
|
||||
if err := createTargetNamespace(ctx, dt.k8sClient, req.Cluster.ClusterName, req.Cluster.Namespace, req.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := createTarget(ctx, dt.ds, &Target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dt.DetailTarget(ctx, &Target)
|
||||
}
|
||||
|
||||
func (dt *targetUsecaseImpl) UpdateTarget(ctx context.Context, target *model.Target, req apisv1.UpdateTargetRequest) (*apisv1.DetailTargetResponse, error) {
|
||||
TargetModel := convertUpdateReqToTargetModel(target, req)
|
||||
if err := dt.ds.Put(ctx, TargetModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dt.DetailTarget(ctx, TargetModel)
|
||||
}
|
||||
|
||||
// DetailTarget detail Target
|
||||
func (dt *targetUsecaseImpl) DetailTarget(ctx context.Context, target *model.Target) (*apisv1.DetailTargetResponse, error) {
|
||||
return &apisv1.DetailTargetResponse{
|
||||
TargetBase: *dt.convertFromTargetModel(ctx, target),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetTarget get Target model
|
||||
func (dt *targetUsecaseImpl) GetTarget(ctx context.Context, targetName string) (*model.Target, error) {
|
||||
Target := &model.Target{
|
||||
Name: targetName,
|
||||
}
|
||||
if err := dt.ds.Get(ctx, Target); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Target, nil
|
||||
}
|
||||
|
||||
func convertUpdateReqToTargetModel(target *model.Target, req apisv1.UpdateTargetRequest) *model.Target {
|
||||
target.Alias = req.Alias
|
||||
target.Description = req.Description
|
||||
target.Variable = req.Variable
|
||||
return target
|
||||
}
|
||||
|
||||
func convertCreateReqToTargetModel(req apisv1.CreateTargetRequest) model.Target {
|
||||
Target := model.Target{
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
Cluster: (*model.ClusterTarget)(req.Cluster),
|
||||
Variable: req.Variable,
|
||||
}
|
||||
return Target
|
||||
}
|
||||
|
||||
func (dt *targetUsecaseImpl) convertFromTargetModel(ctx context.Context, target *model.Target) *apisv1.TargetBase {
|
||||
var appNum int64 = 0
|
||||
// TODO: query app num in target
|
||||
targetBase := &apisv1.TargetBase{
|
||||
Name: target.Name,
|
||||
Alias: target.Alias,
|
||||
Description: target.Description,
|
||||
Cluster: (*apisv1.ClusterTarget)(target.Cluster),
|
||||
Variable: target.Variable,
|
||||
CreateTime: target.CreateTime,
|
||||
UpdateTime: target.UpdateTime,
|
||||
AppNum: appNum,
|
||||
}
|
||||
|
||||
if targetBase.Cluster != nil && targetBase.Cluster.ClusterName != "" {
|
||||
cluster, err := _getClusterFromDataStore(ctx, dt.ds, target.Cluster.ClusterName)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("query cluster info failure %s", err.Error())
|
||||
}
|
||||
if cluster != nil {
|
||||
targetBase.ClusterAlias = cluster.Alias
|
||||
}
|
||||
}
|
||||
|
||||
return targetBase
|
||||
}
|
||||
104
pkg/apiserver/rest/usecase/target_model.go
Normal file
104
pkg/apiserver/rest/usecase/target_model.go
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apierror "k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
velaerr "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
)
|
||||
|
||||
func createTargetNamespace(ctx context.Context, k8sClient client.Client, clusterName, namespace, targetName string) error {
|
||||
err := utils.CreateOrUpdateNamespace(multicluster.ContextWithClusterName(ctx, clusterName), k8sClient, namespace, utils.MergeOverrideLabels(map[string]string{
|
||||
oam.LabelRuntimeNamespaceUsage: oam.VelaNamespaceUsageTarget,
|
||||
}), utils.MergeNoConflictLabels(map[string]string{
|
||||
oam.LabelNamespaceOfTargetName: targetName,
|
||||
}))
|
||||
if velaerr.IsLabelConflict(err) {
|
||||
log.Logger.Errorf("update namespace for target err %v", err)
|
||||
return bcode.ErrTargetNamespaceAlreadyBound
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteTargetNamespace(ctx context.Context, k8sClient client.Client, clusterName, namespace, targetName string) error {
|
||||
err := utils.UpdateNamespace(multicluster.ContextWithClusterName(ctx, clusterName), k8sClient, namespace,
|
||||
// check no conflict label first to make sure the namespace belong to the target, then override it
|
||||
utils.MergeNoConflictLabels(map[string]string{
|
||||
oam.LabelNamespaceOfTargetName: targetName,
|
||||
}),
|
||||
utils.MergeOverrideLabels(map[string]string{
|
||||
oam.LabelRuntimeNamespaceUsage: "",
|
||||
oam.LabelNamespaceOfTargetName: "",
|
||||
}))
|
||||
if apierror.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func createTarget(ctx context.Context, ds datastore.DataStore, tg *model.Target) error {
|
||||
// check Target name.
|
||||
exit, err := ds.IsExist(ctx, tg)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("check target existence failure %s", err.Error())
|
||||
return err
|
||||
}
|
||||
if exit {
|
||||
return bcode.ErrTargetExist
|
||||
}
|
||||
|
||||
if err = ds.Add(ctx, tg); err != nil {
|
||||
log.Logger.Errorf("add target failure %s", err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listTarget(ctx context.Context, ds datastore.DataStore, dsOption *datastore.ListOptions) ([]*model.Target, error) {
|
||||
if dsOption == nil {
|
||||
dsOption = &datastore.ListOptions{}
|
||||
}
|
||||
|
||||
Target := model.Target{}
|
||||
Targets, err := ds.List(ctx, &Target, dsOption)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list target err %v", err)
|
||||
return nil, err
|
||||
}
|
||||
var respTargets []*model.Target
|
||||
for _, raw := range Targets {
|
||||
target, ok := raw.(*model.Target)
|
||||
if ok {
|
||||
respTargets = append(respTargets, target)
|
||||
}
|
||||
}
|
||||
return respTargets, nil
|
||||
}
|
||||
@@ -23,62 +23,65 @@ import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
)
|
||||
|
||||
var _ = Describe("Test delivery target usecase functions", func() {
|
||||
var _ = Describe("Test target usecase functions", func() {
|
||||
var (
|
||||
deliveryTargetUsecase *deliveryTargetUsecaseImpl
|
||||
projectUsecase *projectUsecaseImpl
|
||||
testProject = "target-project"
|
||||
targetUsecase *targetUsecaseImpl
|
||||
projectUsecase *projectUsecaseImpl
|
||||
testProject = "target-project"
|
||||
)
|
||||
BeforeEach(func() {
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
deliveryTargetUsecase = &deliveryTargetUsecaseImpl{ds: ds, projectUsecase: projectUsecase}
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "target-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
targetUsecase = &targetUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
})
|
||||
It("Test CreateDeliveryTarget function", func() {
|
||||
It("Test CreateTarget function", func() {
|
||||
_, err := projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: testProject})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
req := apisv1.CreateDeliveryTargetRequest{
|
||||
Name: "test-delivery-target",
|
||||
Project: testProject,
|
||||
req := apisv1.CreateTargetRequest{
|
||||
Name: "test--target",
|
||||
Alias: "test-alias",
|
||||
Description: "this is a deliveryTarget",
|
||||
Description: "this is a Target",
|
||||
Cluster: &apisv1.ClusterTarget{ClusterName: "cluster-dev", Namespace: "dev"},
|
||||
Variable: map[string]interface{}{"terraform-provider": "provider", "region": "us-1"},
|
||||
}
|
||||
base, err := deliveryTargetUsecase.CreateDeliveryTarget(context.TODO(), req)
|
||||
base, err := targetUsecase.CreateTarget(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
|
||||
|
||||
Expect(deliveryTargetUsecase.ds.Add(context.TODO(), &model.Cluster{Name: "cluster-dev", Alias: "dev-alias"})).Should(Succeed())
|
||||
})
|
||||
Expect(targetUsecase.ds.Add(context.TODO(), &model.Cluster{Name: "cluster-dev", Alias: "dev-alias"})).Should(Succeed())
|
||||
|
||||
It("Test GetDeliveryTarget function", func() {
|
||||
deliveryTarget, err := deliveryTargetUsecase.GetDeliveryTarget(context.TODO(), "test-delivery-target")
|
||||
By("Test GetTarget function")
|
||||
Target, err := targetUsecase.GetTarget(context.TODO(), "test--target")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(deliveryTarget).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff(deliveryTarget.Name, "test-delivery-target")).Should(BeEmpty())
|
||||
})
|
||||
Expect(Target).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff(Target.Name, "test--target")).Should(BeEmpty())
|
||||
|
||||
It("Test ListDeliveryTargets function", func() {
|
||||
resp, err := deliveryTargetUsecase.ListDeliveryTargets(context.TODO(), 1, 1, "")
|
||||
By("Test ListTargets function")
|
||||
resp, err := targetUsecase.ListTargets(context.TODO(), 1, 1)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(resp.Targets[0].ClusterAlias).Should(Equal("dev-alias"))
|
||||
})
|
||||
|
||||
It("Test DetailDeliveryTarget function", func() {
|
||||
detail, err := deliveryTargetUsecase.DetailDeliveryTarget(context.TODO(),
|
||||
&model.DeliveryTarget{
|
||||
Name: "test-delivery-target",
|
||||
Namespace: "test-namespace",
|
||||
By("Test DetailTarget function")
|
||||
detail, err := targetUsecase.DetailTarget(context.TODO(),
|
||||
&model.Target{
|
||||
Name: "test--target",
|
||||
Alias: "test-alias",
|
||||
Description: "this is a deliveryTarget",
|
||||
Description: "this is a Target",
|
||||
Cluster: &model.ClusterTarget{ClusterName: "cluster-dev", Namespace: "dev"},
|
||||
Variable: map[string]interface{}{"terraform-provider": "provider", "region": "us-1"}})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(detail.Name).Should(Equal("test-delivery-target"))
|
||||
Expect(detail.Name).Should(Equal("test--target"))
|
||||
|
||||
By("Test Delete target")
|
||||
err = targetUsecase.DeleteTarget(context.TODO(), "test--target")
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
})
|
||||
118
pkg/apiserver/rest/usecase/testdata/ui-schema.yaml
vendored
118
pkg/apiserver/rest/usecase/testdata/ui-schema.yaml
vendored
@@ -102,23 +102,6 @@
|
||||
label: ReadinessProbe
|
||||
sort: 13
|
||||
subParameters:
|
||||
- description: How often, in seconds, to execute the probe.
|
||||
jsonKey: periodSeconds
|
||||
label: PeriodSeconds
|
||||
sort: 100
|
||||
uiType: Number
|
||||
validate:
|
||||
defaultValue: 10
|
||||
required: true
|
||||
- description: Minimum consecutive successes for the probe to be considered successful
|
||||
after having failed.
|
||||
jsonKey: successThreshold
|
||||
label: SuccessThreshold
|
||||
sort: 100
|
||||
uiType: Number
|
||||
validate:
|
||||
defaultValue: 1
|
||||
required: true
|
||||
- description: Instructions for assessing container health by probing a TCP socket.
|
||||
Either this attribute or the exec attribute or the httpGet attribute MUST be
|
||||
specified. This attribute is mutually exclusive with both the exec attribute
|
||||
@@ -230,6 +213,23 @@
|
||||
validate:
|
||||
defaultValue: 0
|
||||
required: true
|
||||
- description: How often, in seconds, to execute the probe.
|
||||
jsonKey: periodSeconds
|
||||
label: PeriodSeconds
|
||||
sort: 100
|
||||
uiType: Number
|
||||
validate:
|
||||
defaultValue: 10
|
||||
required: true
|
||||
- description: Minimum consecutive successes for the probe to be considered successful
|
||||
after having failed.
|
||||
jsonKey: successThreshold
|
||||
label: SuccessThreshold
|
||||
sort: 100
|
||||
uiType: Number
|
||||
validate:
|
||||
defaultValue: 1
|
||||
required: true
|
||||
uiType: Group
|
||||
validate: {}
|
||||
- description: Instructions for assessing whether the container is alive.
|
||||
@@ -237,6 +237,34 @@
|
||||
label: LivenessProbe
|
||||
sort: 15
|
||||
subParameters:
|
||||
- description: Number of seconds after which the probe times out.
|
||||
jsonKey: timeoutSeconds
|
||||
label: TimeoutSeconds
|
||||
sort: 100
|
||||
uiType: Number
|
||||
validate:
|
||||
defaultValue: 1
|
||||
required: true
|
||||
- description: Instructions for assessing container health by executing a command.
|
||||
Either this attribute or the httpGet attribute or the tcpSocket attribute MUST
|
||||
be specified. This attribute is mutually exclusive with both the httpGet attribute
|
||||
and the tcpSocket attribute.
|
||||
jsonKey: exec
|
||||
label: Exec
|
||||
sort: 100
|
||||
subParameters:
|
||||
- description: A command to be executed inside the container to assess its health.
|
||||
Each space delimited token of the command is a separate array element. Commands
|
||||
exiting 0 are considered to be successful probes, whilst all other exit codes
|
||||
are considered failures.
|
||||
jsonKey: command
|
||||
label: Command
|
||||
sort: 100
|
||||
uiType: Strings
|
||||
validate:
|
||||
required: true
|
||||
uiType: Group
|
||||
validate: {}
|
||||
- description: Number of consecutive failures required to determine the container
|
||||
is not alive (liveness probe) or not ready (readiness probe).
|
||||
jsonKey: failureThreshold
|
||||
@@ -254,6 +282,14 @@
|
||||
label: HttpGet
|
||||
sort: 100
|
||||
subParameters:
|
||||
- description: The endpoint, relative to the port, to which the HTTP GET request
|
||||
should be directed.
|
||||
jsonKey: path
|
||||
label: Path
|
||||
sort: 100
|
||||
uiType: Input
|
||||
validate:
|
||||
required: true
|
||||
- description: The TCP socket within the container to which the HTTP GET request
|
||||
should be directed.
|
||||
jsonKey: port
|
||||
@@ -283,14 +319,6 @@
|
||||
required: true
|
||||
uiType: Structs
|
||||
validate: {}
|
||||
- description: The endpoint, relative to the port, to which the HTTP GET request
|
||||
should be directed.
|
||||
jsonKey: path
|
||||
label: Path
|
||||
sort: 100
|
||||
uiType: Input
|
||||
validate:
|
||||
required: true
|
||||
uiType: Group
|
||||
validate: {}
|
||||
- description: Number of seconds after the container is started before the first
|
||||
@@ -337,36 +365,14 @@
|
||||
required: true
|
||||
uiType: Group
|
||||
validate: {}
|
||||
- description: Number of seconds after which the probe times out.
|
||||
jsonKey: timeoutSeconds
|
||||
label: TimeoutSeconds
|
||||
sort: 100
|
||||
uiType: Number
|
||||
validate:
|
||||
defaultValue: 1
|
||||
required: true
|
||||
- description: Instructions for assessing container health by executing a command.
|
||||
Either this attribute or the httpGet attribute or the tcpSocket attribute MUST
|
||||
be specified. This attribute is mutually exclusive with both the httpGet attribute
|
||||
and the tcpSocket attribute.
|
||||
jsonKey: exec
|
||||
label: Exec
|
||||
sort: 100
|
||||
subParameters:
|
||||
- description: A command to be executed inside the container to assess its health.
|
||||
Each space delimited token of the command is a separate array element. Commands
|
||||
exiting 0 are considered to be successful probes, whilst all other exit codes
|
||||
are considered failures.
|
||||
jsonKey: command
|
||||
label: Command
|
||||
sort: 100
|
||||
uiType: Strings
|
||||
validate:
|
||||
required: true
|
||||
uiType: Group
|
||||
validate: {}
|
||||
uiType: Group
|
||||
validate: {}
|
||||
- description: Specify image pull secrets for your service
|
||||
jsonKey: imagePullSecrets
|
||||
label: ImagePullSecrets
|
||||
sort: 100
|
||||
uiType: Strings
|
||||
validate: {}
|
||||
- description: Which port do you want customer traffic sent to
|
||||
disable: true
|
||||
jsonKey: port
|
||||
@@ -376,12 +382,6 @@
|
||||
validate:
|
||||
defaultValue: 80
|
||||
required: true
|
||||
- description: Specify image pull secrets for your service
|
||||
jsonKey: imagePullSecrets
|
||||
label: ImagePullSecrets
|
||||
sort: 100
|
||||
uiType: Strings
|
||||
validate: {}
|
||||
- description: Declare volumes and volumeMounts
|
||||
disable: true
|
||||
jsonKey: volumes
|
||||
|
||||
@@ -19,6 +19,7 @@ package usecase
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
@@ -37,6 +38,7 @@ type VelaQLUsecase interface {
|
||||
|
||||
type velaQLUsecaseImpl struct {
|
||||
kubeClient client.Client
|
||||
kubeConfig *rest.Config
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
}
|
||||
@@ -48,6 +50,11 @@ func NewVelaQLUsecase() VelaQLUsecase {
|
||||
log.Logger.Fatalf("get kubeclient failure %s", err.Error())
|
||||
}
|
||||
|
||||
kubeConfig, err := clients.GetKubeConfig()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get kubeconfig failure %s", err.Error())
|
||||
}
|
||||
|
||||
dm, err := clients.GetDiscoverMapper()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get discover mapper failure %s", err.Error())
|
||||
@@ -59,6 +66,7 @@ func NewVelaQLUsecase() VelaQLUsecase {
|
||||
}
|
||||
return &velaQLUsecaseImpl{
|
||||
kubeClient: k8sClient,
|
||||
kubeConfig: kubeConfig,
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
}
|
||||
@@ -71,7 +79,7 @@ func (v *velaQLUsecaseImpl) QueryView(ctx context.Context, velaQL string) (*apis
|
||||
return nil, bcode.ErrParseVelaQL
|
||||
}
|
||||
|
||||
queryValue, err := velaql.NewViewHandler(v.kubeClient, v.dm, v.pd).QueryView(ctx, query)
|
||||
queryValue, err := velaql.NewViewHandler(v.kubeClient, v.kubeConfig, v.dm, v.pd).QueryView(ctx, query)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("fail to query the view %s", err.Error())
|
||||
return nil, bcode.ErrViewQuery
|
||||
|
||||
111
pkg/apiserver/rest/usecase/webhook.go
Normal file
111
pkg/apiserver/rest/usecase/webhook.go
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/policy/envbinding"
|
||||
)
|
||||
|
||||
// WebhookUsecase webhook usecase
|
||||
type WebhookUsecase interface {
|
||||
HandleApplicationWebhook(ctx context.Context, token string, req *restful.Request) (*apisv1.ApplicationDeployResponse, error)
|
||||
}
|
||||
|
||||
type webhookUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
applicationUsecase ApplicationUsecase
|
||||
}
|
||||
|
||||
// NewWebhookUsecase new webhook usecase
|
||||
func NewWebhookUsecase(ds datastore.DataStore,
|
||||
applicationUsecase ApplicationUsecase,
|
||||
) WebhookUsecase {
|
||||
return &webhookUsecaseImpl{
|
||||
ds: ds,
|
||||
applicationUsecase: applicationUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *webhookUsecaseImpl) HandleApplicationWebhook(ctx context.Context, token string, req *restful.Request) (*apisv1.ApplicationDeployResponse, error) {
|
||||
webhookTrigger := &model.ApplicationTrigger{
|
||||
Token: token,
|
||||
}
|
||||
if err := c.ds.Get(ctx, webhookTrigger); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrInvalidWebhookToken
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
app := &model.Application{
|
||||
Name: webhookTrigger.AppPrimaryKey,
|
||||
}
|
||||
if err := c.ds.Get(ctx, app); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrApplicationNotExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
switch webhookTrigger.PayloadType {
|
||||
case model.PayloadTypeCustom:
|
||||
var webhookReq apisv1.HandleApplicationWebhookRequest
|
||||
if err := req.ReadEntity(&webhookReq); err != nil {
|
||||
return nil, bcode.ErrInvalidWebhookPayloadBody
|
||||
}
|
||||
for comp, properties := range webhookReq.Upgrade {
|
||||
component := &model.ApplicationComponent{
|
||||
AppPrimaryKey: webhookTrigger.AppPrimaryKey,
|
||||
Name: comp,
|
||||
}
|
||||
if err := c.ds.Get(ctx, component); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrApplicationComponetNotExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
merge, err := envbinding.MergeRawExtension(component.Properties.RawExtension(), properties.RawExtension())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prop, err := model.NewJSONStructByStruct(merge)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
component.Properties = prop
|
||||
if err := c.ds.Put(ctx, component); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.applicationUsecase.Deploy(ctx, app, apisv1.ApplicationDeployRequest{
|
||||
WorkflowName: webhookTrigger.WorkflowName,
|
||||
Note: "triggered by webhook",
|
||||
TriggerType: apisv1.TriggerTypeWebhook,
|
||||
Force: true,
|
||||
CodeInfo: webhookReq.CodeInfo,
|
||||
})
|
||||
default:
|
||||
return nil, bcode.ErrInvalidWebhookPayloadType
|
||||
}
|
||||
}
|
||||
148
pkg/apiserver/rest/usecase/webhook_test.go
Normal file
148
pkg/apiserver/rest/usecase/webhook_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
var _ = Describe("Test application usecase function", func() {
|
||||
var (
|
||||
appUsecase *applicationUsecaseImpl
|
||||
workflowUsecase *workflowUsecaseImpl
|
||||
envUsecase *envUsecaseImpl
|
||||
envBindingUsecase *envBindingUsecaseImpl
|
||||
targetUsecase *targetUsecaseImpl
|
||||
definitionUsecase *definitionUsecaseImpl
|
||||
projectUsecase *projectUsecaseImpl
|
||||
webhookUsecase *webhookUsecaseImpl
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "app-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
workflowUsecase = &workflowUsecaseImpl{ds: ds, envUsecase: envUsecase}
|
||||
definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient}
|
||||
envBindingUsecase = &envBindingUsecaseImpl{ds: ds, envUsecase: envUsecase, workflowUsecase: workflowUsecase, kubeClient: k8sClient, definitionUsecase: definitionUsecase}
|
||||
targetUsecase = &targetUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
appUsecase = &applicationUsecaseImpl{
|
||||
ds: ds,
|
||||
workflowUsecase: workflowUsecase,
|
||||
apply: apply.NewAPIApplicator(k8sClient),
|
||||
kubeClient: k8sClient,
|
||||
envBindingUsecase: envBindingUsecase,
|
||||
envUsecase: envUsecase,
|
||||
definitionUsecase: definitionUsecase,
|
||||
targetUsecase: targetUsecase,
|
||||
projectUsecase: projectUsecase,
|
||||
}
|
||||
webhookUsecase = &webhookUsecaseImpl{
|
||||
ds: ds,
|
||||
applicationUsecase: appUsecase,
|
||||
}
|
||||
})
|
||||
|
||||
It("Test HandleApplicationWebhook function", func() {
|
||||
_, err := targetUsecase.CreateTarget(context.TODO(), apisv1.CreateTargetRequest{Name: "dev-target-webhook"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: "project-webhook"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "webhook-dev", Namespace: "webhook-dev", Targets: []string{"dev-target-webhook"}, Project: "project-webhook"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
Expect(err).Should(BeNil())
|
||||
req := apisv1.CreateApplicationRequest{
|
||||
Name: "test-app-webhook",
|
||||
Project: "project-webhook",
|
||||
Description: "this is a test app",
|
||||
EnvBinding: []*apisv1.EnvBinding{{
|
||||
Name: "webhook-dev",
|
||||
}},
|
||||
Component: &apisv1.CreateComponentRequest{
|
||||
Name: "component-name-webhook",
|
||||
ComponentType: "webservice",
|
||||
},
|
||||
}
|
||||
_, err = appUsecase.CreateApplication(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
appModel, err := appUsecase.GetApplication(context.TODO(), "test-app-webhook")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = webhookUsecase.HandleApplicationWebhook(context.TODO(), "invalid-token", nil)
|
||||
Expect(err).Should(Equal(bcode.ErrInvalidWebhookToken))
|
||||
|
||||
triggers, err := appUsecase.ListApplicationTriggers(context.TODO(), appModel)
|
||||
Expect(err).Should(BeNil())
|
||||
reqBody := apisv1.HandleApplicationWebhookRequest{
|
||||
Upgrade: map[string]*model.JSONStruct{
|
||||
"component-name-webhook": {
|
||||
"image": "test-image",
|
||||
"test1": map[string]string{
|
||||
"test2": "test3",
|
||||
},
|
||||
},
|
||||
},
|
||||
CodeInfo: &model.CodeInfo{
|
||||
Commit: "test-commit",
|
||||
Branch: "test-branch",
|
||||
User: "test-user",
|
||||
},
|
||||
}
|
||||
body, err := json.Marshal(reqBody)
|
||||
Expect(err).Should(BeNil())
|
||||
httpreq, err := http.NewRequest("post", "/", bytes.NewBuffer(body))
|
||||
httpreq.Header.Add(restful.HEADER_ContentType, "application/json")
|
||||
Expect(err).Should(BeNil())
|
||||
res, err := webhookUsecase.HandleApplicationWebhook(context.TODO(), triggers[0].Token, restful.NewRequest(httpreq))
|
||||
Expect(err).Should(BeNil())
|
||||
comp, err := appUsecase.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect((*comp.Properties)["image"]).Should(Equal("test-image"))
|
||||
Expect((*comp.Properties)["test1"]).Should(Equal(map[string]interface{}{
|
||||
"test2": "test3",
|
||||
}))
|
||||
|
||||
revision := &model.ApplicationRevision{
|
||||
AppPrimaryKey: "test-app-webhook",
|
||||
Version: res.Version,
|
||||
}
|
||||
err = webhookUsecase.ds.Get(context.TODO(), revision)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(revision.CodeInfo.Commit).Should(Equal("test-commit"))
|
||||
Expect(revision.CodeInfo.Branch).Should(Equal("test-branch"))
|
||||
Expect(revision.CodeInfo.User).Should(Equal("test-user"))
|
||||
})
|
||||
})
|
||||
@@ -65,7 +65,7 @@ type WorkflowUsecase interface {
|
||||
}
|
||||
|
||||
// NewWorkflowUsecase new workflow usecase
|
||||
func NewWorkflowUsecase(ds datastore.DataStore) WorkflowUsecase {
|
||||
func NewWorkflowUsecase(ds datastore.DataStore, envUsecase EnvUsecase) WorkflowUsecase {
|
||||
kubecli, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get kubeclient failure %s", err.Error())
|
||||
@@ -74,6 +74,7 @@ func NewWorkflowUsecase(ds datastore.DataStore) WorkflowUsecase {
|
||||
ds: ds,
|
||||
kubeClient: kubecli,
|
||||
apply: apply.NewAPIApplicator(kubecli),
|
||||
envUsecase: envUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +82,7 @@ type workflowUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
kubeClient client.Client
|
||||
apply apply.Applicator
|
||||
envUsecase EnvUsecase
|
||||
}
|
||||
|
||||
// DeleteWorkflow delete application workflow
|
||||
@@ -198,72 +200,38 @@ func (w *workflowUsecaseImpl) CreateOrUpdateWorkflow(ctx context.Context, app *m
|
||||
return w.DetailWorkflow(ctx, workflow)
|
||||
}
|
||||
|
||||
func (w *workflowUsecaseImpl) UpdateWorkflow(ctx context.Context, workflow *model.Workflow, req apisv1.UpdateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error) {
|
||||
var steps []model.WorkflowStep
|
||||
for _, step := range req.Steps {
|
||||
properties, err := model.NewJSONStructByString(step.Properties)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("parse trait properties failire %w", err)
|
||||
return nil, bcode.ErrInvalidProperties
|
||||
}
|
||||
steps = append(steps, model.WorkflowStep{
|
||||
Name: step.Name,
|
||||
Alias: step.Alias,
|
||||
Description: step.Description,
|
||||
DependsOn: step.DependsOn,
|
||||
Type: step.Type,
|
||||
Inputs: step.Inputs,
|
||||
Outputs: step.Outputs,
|
||||
Properties: properties,
|
||||
})
|
||||
}
|
||||
// updateWorkflowSteps will update workflow with new steps
|
||||
func updateWorkflowSteps(ctx context.Context, ds datastore.DataStore, workflow *model.Workflow, steps []model.WorkflowStep) error {
|
||||
workflow.Steps = steps
|
||||
return ds.Put(ctx, workflow)
|
||||
}
|
||||
|
||||
func (w *workflowUsecaseImpl) UpdateWorkflow(ctx context.Context, workflow *model.Workflow, req apisv1.UpdateWorkflowRequest) (*apisv1.DetailWorkflowResponse, error) {
|
||||
modeSteps, err := convertAPIStep2ModelStep(req.Steps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workflow.Description = req.Description
|
||||
// It is allowed to set multiple workflows as default, and only one takes effect.
|
||||
workflow.Default = &req.Default
|
||||
if err := w.ds.Put(ctx, workflow); err != nil {
|
||||
if req.Default != nil {
|
||||
workflow.Default = req.Default
|
||||
}
|
||||
if err := updateWorkflowSteps(ctx, w.ds, workflow, modeSteps); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return w.DetailWorkflow(ctx, workflow)
|
||||
}
|
||||
|
||||
func converWorkflowBase(workflow *model.Workflow) apisv1.WorkflowBase {
|
||||
var steps []apisv1.WorkflowStep
|
||||
for _, step := range workflow.Steps {
|
||||
steps = append(steps, convertFromWorkflowStepModel(step))
|
||||
}
|
||||
return apisv1.WorkflowBase{
|
||||
Name: workflow.Name,
|
||||
Alias: workflow.Alias,
|
||||
Description: workflow.Description,
|
||||
Default: convertBool(workflow.Default),
|
||||
EnvName: workflow.EnvName,
|
||||
CreateTime: workflow.CreateTime,
|
||||
UpdateTime: workflow.UpdateTime,
|
||||
Steps: steps,
|
||||
}
|
||||
}
|
||||
|
||||
// DetailWorkflow detail workflow
|
||||
func (w *workflowUsecaseImpl) DetailWorkflow(ctx context.Context, workflow *model.Workflow) (*apisv1.DetailWorkflowResponse, error) {
|
||||
return &apisv1.DetailWorkflowResponse{
|
||||
WorkflowBase: converWorkflowBase(workflow),
|
||||
WorkflowBase: convertWorkflowBase(workflow),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetWorkflow get workflow model
|
||||
func (w *workflowUsecaseImpl) GetWorkflow(ctx context.Context, app *model.Application, workflowName string) (*model.Workflow, error) {
|
||||
var workflow = model.Workflow{
|
||||
Name: workflowName,
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
}
|
||||
if err := w.ds.Get(ctx, &workflow); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrWorkflowNotExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &workflow, nil
|
||||
return getWorkflowForApp(ctx, w.ds, app, workflowName)
|
||||
}
|
||||
|
||||
// ListApplicationWorkflow list application workflows
|
||||
@@ -278,7 +246,7 @@ func (w *workflowUsecaseImpl) ListApplicationWorkflow(ctx context.Context, app *
|
||||
var list []*apisv1.WorkflowBase
|
||||
for _, workflow := range workflows {
|
||||
wm := workflow.(*model.Workflow)
|
||||
base := converWorkflowBase(wm)
|
||||
base := convertWorkflowBase(wm)
|
||||
list = append(list, &base)
|
||||
}
|
||||
return list, nil
|
||||
@@ -387,7 +355,7 @@ func (w *workflowUsecaseImpl) SyncWorkflowRecord(ctx context.Context) error {
|
||||
klog.ErrorS(err, "failed to get workflow", "app name", record.AppPrimaryKey, "workflow name", record.WorkflowName, "record name", record.Name)
|
||||
continue
|
||||
}
|
||||
appName := convertAppName(record.AppPrimaryKey, workflow.EnvName)
|
||||
appName := record.AppPrimaryKey
|
||||
if err := w.kubeClient.Get(ctx, types.NamespacedName{
|
||||
Name: appName,
|
||||
Namespace: record.Namespace,
|
||||
@@ -519,7 +487,7 @@ func (w *workflowUsecaseImpl) CreateWorkflowRecord(ctx context.Context, appModel
|
||||
AppPrimaryKey: appModel.PrimaryKey(),
|
||||
RevisionPrimaryKey: app.Annotations[oam.AnnotationDeployVersion],
|
||||
Name: app.Annotations[oam.AnnotationPublishVersion],
|
||||
Namespace: appModel.Namespace,
|
||||
Namespace: app.Namespace,
|
||||
Finished: "false",
|
||||
StartTime: time.Now().Time,
|
||||
Steps: steps,
|
||||
@@ -635,8 +603,9 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
var revision = model.ApplicationRevision{
|
||||
AppPrimaryKey: appModel.Name,
|
||||
Status: model.RevisionStatusComplete,
|
||||
WorkflowName: workflow.Name,
|
||||
EnvName: workflow.EnvName,
|
||||
}
|
||||
|
||||
revisions, err := w.ds.List(ctx, &revision, &datastore.ListOptions{
|
||||
Page: 1,
|
||||
PageSize: 1,
|
||||
@@ -649,6 +618,7 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
return bcode.ErrApplicationNoReadyRevision
|
||||
}
|
||||
revisionVersion = revisions[0].Index()["version"]
|
||||
log.Logger.Infof("select lastest complete revision %s", revisions[0].Index()["version"])
|
||||
}
|
||||
|
||||
var record = &model.WorkflowRecord{
|
||||
@@ -719,7 +689,11 @@ func (w *workflowUsecaseImpl) RollbackRecord(ctx context.Context, appModel *mode
|
||||
|
||||
func (w *workflowUsecaseImpl) checkRecordRunning(ctx context.Context, appModel *model.Application, envName string) (*v1beta1.Application, error) {
|
||||
oamApp := &v1beta1.Application{}
|
||||
if err := w.kubeClient.Get(ctx, types.NamespacedName{Name: convertAppName(appModel.Name, envName), Namespace: appModel.Namespace}, oamApp); err != nil {
|
||||
env, err := w.envUsecase.GetEnv(ctx, envName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := w.kubeClient.Get(ctx, types.NamespacedName{Name: appModel.Name, Namespace: env.Namespace}, oamApp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if oamApp.Status.Workflow != nil && !oamApp.Status.Workflow.Suspend && !oamApp.Status.Workflow.Terminated && !oamApp.Status.Workflow.Finished {
|
||||
|
||||
171
pkg/apiserver/rest/usecase/workflow_model.go
Normal file
171
pkg/apiserver/rest/usecase/workflow_model.go
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
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 usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
utils2 "github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// UpdateEnvWorkflow will update env workflow internally
|
||||
func UpdateEnvWorkflow(ctx context.Context, kubeClient client.Client, ds datastore.DataStore, app *model.Application, env *model.Env) error {
|
||||
// The existing step configuration should be maintained and the delivery target steps should be automatically updated.
|
||||
envSteps := GenEnvWorkflowSteps(ctx, kubeClient, ds, env, app)
|
||||
workflow, err := getWorkflowForApp(ctx, ds, app, convertWorkflowName(env.Name))
|
||||
if err != nil {
|
||||
// no workflow exist mean no need to update
|
||||
if errors.Is(err, bcode.ErrWorkflowNotExist) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var envStepNames = env.Targets
|
||||
var workflowStepNames []string
|
||||
for _, step := range workflow.Steps {
|
||||
if isEnvStepType(step.Type) {
|
||||
workflowStepNames = append(workflowStepNames, step.Name)
|
||||
}
|
||||
}
|
||||
|
||||
var filteredSteps []apisv1.WorkflowStep
|
||||
_, readyToDeleteSteps, readyToAddSteps := compareSlices(workflowStepNames, envStepNames)
|
||||
|
||||
for _, step := range workflow.Steps {
|
||||
if isEnvStepType(step.Type) && utils.StringsContain(readyToDeleteSteps, step.Name) {
|
||||
continue
|
||||
}
|
||||
filteredSteps = append(filteredSteps, convertFromWorkflowStepModel(step))
|
||||
}
|
||||
|
||||
for _, step := range envSteps {
|
||||
if isEnvStepType(step.Type) && utils.StringsContain(readyToAddSteps, step.Name) {
|
||||
filteredSteps = append(filteredSteps, step)
|
||||
}
|
||||
}
|
||||
modelSteps, err := convertAPIStep2ModelStep(filteredSteps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = updateWorkflowSteps(ctx, ds, workflow, modelSteps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetComponentDefinition will get componentDefinition by kube client
|
||||
func GetComponentDefinition(ctx context.Context, kubeClient client.Client, name string) (*v1beta1.ComponentDefinition, error) {
|
||||
var componentDefinition v1beta1.ComponentDefinition
|
||||
if err := kubeClient.Get(ctx, k8stypes.NamespacedName{Namespace: types.DefaultKubeVelaNS, Name: name}, &componentDefinition); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &componentDefinition, nil
|
||||
}
|
||||
|
||||
// GetSuitableDeployWay will get a workflow deploy strategy for workflow step
|
||||
func GetSuitableDeployWay(ctx context.Context, kubeClient client.Client, ds datastore.DataStore, app *model.Application) string {
|
||||
components, err := ds.List(ctx, &model.ApplicationComponent{AppPrimaryKey: app.PrimaryKey()}, &datastore.ListOptions{PageSize: 1, Page: 1})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list application component list failure %s", err.Error())
|
||||
}
|
||||
if len(components) > 0 {
|
||||
component := components[0].(*model.ApplicationComponent)
|
||||
definition, err := GetComponentDefinition(ctx, kubeClient, component.Type)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("get component definition %s failure %s", component.Type, err.Error())
|
||||
// using Deploy2Env by default
|
||||
}
|
||||
if definition != nil {
|
||||
if definition.Spec.Workload.Type == TerraformWorkfloadType {
|
||||
return DeployCloudResource
|
||||
}
|
||||
if definition.Spec.Workload.Definition.Kind == TerraformWorkfloadKind {
|
||||
return DeployCloudResource
|
||||
}
|
||||
}
|
||||
}
|
||||
return Deploy2Env
|
||||
}
|
||||
|
||||
// GenEnvWorkflowSteps will generate workflow steps for an env and application
|
||||
func GenEnvWorkflowSteps(ctx context.Context, kubeClient client.Client, ds datastore.DataStore, env *model.Env, app *model.Application) []apisv1.WorkflowStep {
|
||||
var workflowSteps []v1beta1.WorkflowStep
|
||||
for _, targetName := range env.Targets {
|
||||
step := v1beta1.WorkflowStep{
|
||||
Name: genPolicyEnvName(targetName),
|
||||
Type: GetSuitableDeployWay(ctx, kubeClient, ds, app),
|
||||
Properties: util.Object2RawExtension(map[string]string{
|
||||
"policy": genPolicyName(env.Name),
|
||||
"env": genPolicyEnvName(targetName),
|
||||
}),
|
||||
}
|
||||
workflowSteps = append(workflowSteps, step)
|
||||
}
|
||||
var steps []apisv1.WorkflowStep
|
||||
for _, step := range workflowSteps {
|
||||
var propertyStr string
|
||||
if step.Properties != nil {
|
||||
properties, err := model.NewJSONStruct(step.Properties)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("workflow %s step %s properties is invalid %s", utils2.Sanitize(app.Name), utils2.Sanitize(step.Name), err.Error())
|
||||
continue
|
||||
}
|
||||
propertyStr = properties.JSON()
|
||||
}
|
||||
steps = append(steps, apisv1.WorkflowStep{
|
||||
Name: step.Name,
|
||||
Type: step.Type,
|
||||
Alias: fmt.Sprintf("Deploy To %s", step.Name),
|
||||
Description: fmt.Sprintf("deploy app to delivery target %s", step.Name),
|
||||
DependsOn: step.DependsOn,
|
||||
Properties: propertyStr,
|
||||
Inputs: step.Inputs,
|
||||
Outputs: step.Outputs,
|
||||
})
|
||||
}
|
||||
return steps
|
||||
}
|
||||
|
||||
func getWorkflowForApp(ctx context.Context, ds datastore.DataStore, app *model.Application, workflowName string) (*model.Workflow, error) {
|
||||
var workflow = model.Workflow{
|
||||
Name: workflowName,
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
}
|
||||
if err := ds.Get(ctx, &workflow); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrWorkflowNotExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &workflow, nil
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
. "github.com/onsi/ginkgo"
|
||||
@@ -32,6 +34,7 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
@@ -44,19 +47,32 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
workflowUsecase *workflowUsecaseImpl
|
||||
appUsecase *applicationUsecaseImpl
|
||||
projectUsecase *projectUsecaseImpl
|
||||
envUsecase *envUsecaseImpl
|
||||
testProject = "workflow-project"
|
||||
ds datastore.DataStore
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
workflowUsecase = &workflowUsecaseImpl{ds: ds, kubeClient: k8sClient, apply: apply.NewAPIApplicator(k8sClient)}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
var err error
|
||||
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "workflow-test-" + strconv.FormatInt(time.Now().UnixNano(), 10)})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
workflowUsecase = &workflowUsecaseImpl{
|
||||
ds: ds,
|
||||
kubeClient: k8sClient,
|
||||
apply: apply.NewAPIApplicator(k8sClient),
|
||||
envUsecase: envUsecase}
|
||||
appUsecase = &applicationUsecaseImpl{ds: ds, kubeClient: k8sClient,
|
||||
apply: apply.NewAPIApplicator(k8sClient),
|
||||
projectUsecase: projectUsecase,
|
||||
envUsecase: envUsecase,
|
||||
envBindingUsecase: &envBindingUsecaseImpl{
|
||||
ds: ds,
|
||||
workflowUsecase: workflowUsecase,
|
||||
envUsecase: envUsecase,
|
||||
}}
|
||||
|
||||
})
|
||||
It("Test CreateWorkflow function", func() {
|
||||
_, err := projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: testProject})
|
||||
@@ -66,9 +82,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Project: testProject,
|
||||
Description: "this is a test app",
|
||||
EnvBinding: []*apisv1.EnvBinding{{
|
||||
Name: "dev",
|
||||
Description: "dev env",
|
||||
TargetNames: []string{"dev-target"},
|
||||
Name: "dev",
|
||||
}},
|
||||
}
|
||||
_, err = appUsecase.CreateApplication(context.TODO(), reqApp)
|
||||
@@ -80,8 +94,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
}
|
||||
|
||||
base, err := workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
|
||||
@@ -93,8 +106,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
}
|
||||
|
||||
base, err = workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, req2)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Description, req2.Description)).Should(BeEmpty())
|
||||
@@ -117,23 +129,19 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Default: &defaultW,
|
||||
}
|
||||
base, err = workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test GetApplicationDefaultWorkflow function", func() {
|
||||
By("Test GetApplicationDefaultWorkflow function")
|
||||
workflow, err := workflowUsecase.GetApplicationDefaultWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(workflow).ShouldNot(BeNil())
|
||||
})
|
||||
|
||||
It("Test ListWorkflowRecords function", func() {
|
||||
By("Test ListWorkflowRecords function")
|
||||
By("create some workflow records to test list workflow records")
|
||||
raw, err := yaml.YAMLToJSON([]byte(yamlStr))
|
||||
Expect(err).Should(BeNil())
|
||||
@@ -141,17 +149,15 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
err = json.Unmarshal(raw, app)
|
||||
Expect(err).Should(BeNil())
|
||||
app.Annotations[oam.AnnotationWorkflowName] = "test-workflow-2"
|
||||
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
workflow, err = workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, "test-workflow-2")
|
||||
Expect(err).Should(BeNil())
|
||||
for i := 0; i < 3; i++ {
|
||||
app.Annotations[oam.AnnotationPublishVersion] = fmt.Sprintf("list-workflow-name-%d", i)
|
||||
app.Status.Workflow.AppRevision = fmt.Sprintf("list-workflow-name-%d", i)
|
||||
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
}
|
||||
@@ -159,26 +165,22 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
resp, err := workflowUsecase.ListWorkflowRecords(context.TODO(), workflow, 0, 10)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(resp.Total).Should(Equal(int64(3)))
|
||||
})
|
||||
|
||||
It("Test DetailWorkflowRecord function", func() {
|
||||
By("create one workflow record to test detail workflow record")
|
||||
raw, err := yaml.YAMLToJSON([]byte(yamlStr))
|
||||
raw, err = yaml.YAMLToJSON([]byte(yamlStr))
|
||||
Expect(err).Should(BeNil())
|
||||
app := &v1beta1.Application{}
|
||||
app = &v1beta1.Application{}
|
||||
err = json.Unmarshal(raw, app)
|
||||
Expect(err).Should(BeNil())
|
||||
app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-2-123"
|
||||
app.Status.Workflow.AppRevision = "test-workflow-2-123"
|
||||
app.Annotations[oam.AnnotationDeployVersion] = "1234"
|
||||
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
workflow, err = workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, "test-workflow-2")
|
||||
Expect(err).Should(BeNil())
|
||||
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -199,13 +201,11 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(detail.WorkflowRecord.Name).Should(Equal("test-workflow-2-123"))
|
||||
Expect(detail.DeployUser).Should(Equal("test-user"))
|
||||
})
|
||||
|
||||
It("Test SyncWorkflowRecord function", func() {
|
||||
By("create one workflow record to test sync status from application")
|
||||
raw, err := yaml.YAMLToJSON([]byte(yamlStr))
|
||||
raw, err = yaml.YAMLToJSON([]byte(yamlStr))
|
||||
Expect(err).Should(BeNil())
|
||||
app := &v1beta1.Application{}
|
||||
app = &v1beta1.Application{}
|
||||
err = json.Unmarshal(raw, app)
|
||||
Expect(err).Should(BeNil())
|
||||
app.Status.Workflow.Finished = false
|
||||
@@ -213,19 +213,17 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
app.Annotations[oam.AnnotationPublishVersion] = "test-workflow-2-233"
|
||||
app.Status.Workflow.AppRevision = "test-workflow-2-233"
|
||||
app.Annotations[oam.AnnotationDeployVersion] = "4321"
|
||||
workflow, err := workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
workflow, err = workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
}, "test-workflow-2")
|
||||
Expect(err).Should(BeNil())
|
||||
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
By("create one revision to test sync workflow record")
|
||||
var revision = &model.ApplicationRevision{
|
||||
revision = &model.ApplicationRevision{
|
||||
AppPrimaryKey: appName,
|
||||
Version: "4321",
|
||||
Status: model.RevisionStatusInit,
|
||||
@@ -246,8 +244,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
workflow, err = workflowUsecase.GetWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, "test-workflow-2")
|
||||
Expect(err).Should(BeNil())
|
||||
By("check the record")
|
||||
@@ -270,8 +267,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
app.Status.Workflow.AppRevision = "test-workflow-2-111"
|
||||
app.Annotations[oam.AnnotationDeployVersion] = "1111"
|
||||
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -290,7 +286,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
cr := &appsv1.ControllerRevision{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "record-app-workflow-dev-test-workflow-2-111",
|
||||
Name: "record-app-workflow-test-workflow-2-111",
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{"vela.io/wf-revision": "test-workflow-2-111"},
|
||||
},
|
||||
@@ -324,12 +320,11 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
})
|
||||
}
|
||||
|
||||
app, err := createTestSuspendApp(ctx, "record-app", "dev", "revision-123", "test-workflow", "test-record-3", workflowUsecase.kubeClient)
|
||||
app, err := createTestSuspendApp(ctx, "record-app", "default", "revision-123", "test-workflow", "test-record-3", workflowUsecase.kubeClient)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowUsecase.CreateWorkflowRecord(ctx, &model.Application{
|
||||
Name: "record-app",
|
||||
Namespace: "default",
|
||||
Name: "record-app",
|
||||
}, app, &model.Workflow{Name: "test-workflow"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -346,6 +341,8 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
It("Test ResumeRecord function", func() {
|
||||
ctx := context.TODO()
|
||||
|
||||
_, err := envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "resume"})
|
||||
Expect(err).Should(BeNil())
|
||||
ResumeWorkflow := "resume-workflow"
|
||||
req := apisv1.CreateWorkflowRequest{
|
||||
Name: ResumeWorkflow,
|
||||
@@ -354,8 +351,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
}
|
||||
|
||||
base, err := workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
|
||||
@@ -364,8 +360,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, app, &model.Workflow{Name: ResumeWorkflow})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -377,8 +372,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowUsecase.ResumeRecord(ctx, &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, &model.Workflow{Name: ResumeWorkflow, EnvName: "resume"}, "workflow-resume-1")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -390,6 +384,8 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
It("Test TerminateRecord function", func() {
|
||||
ctx := context.TODO()
|
||||
|
||||
_, err := envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "terminate"})
|
||||
Expect(err).Should(BeNil())
|
||||
workflowName := "terminate-workflow"
|
||||
req := apisv1.CreateWorkflowRequest{
|
||||
Name: workflowName,
|
||||
@@ -398,8 +394,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
}
|
||||
workflow := &model.Workflow{Name: workflowName, EnvName: "terminate"}
|
||||
base, err := workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
|
||||
@@ -408,8 +403,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -421,8 +415,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowUsecase.TerminateRecord(ctx, &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, workflow, "test-workflow-2-1")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -433,7 +426,8 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
|
||||
It("Test RollbackRecord function", func() {
|
||||
ctx := context.TODO()
|
||||
|
||||
_, err := envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "rollback"})
|
||||
Expect(err).Should(BeNil())
|
||||
workflowName := "rollback-workflow"
|
||||
req := apisv1.CreateWorkflowRequest{
|
||||
Name: workflowName,
|
||||
@@ -442,8 +436,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
}
|
||||
workflow := &model.Workflow{Name: workflowName, EnvName: "rollback"}
|
||||
base, err := workflowUsecase.CreateOrUpdateWorkflow(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Name, req.Name)).Should(BeEmpty())
|
||||
@@ -452,8 +445,7 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -461,6 +453,8 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
AppPrimaryKey: appName,
|
||||
Version: "revision-rollback1",
|
||||
Status: model.RevisionStatusRunning,
|
||||
WorkflowName: workflow.Name,
|
||||
EnvName: "rollback",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
err = workflowUsecase.createTestApplicationRevision(ctx, &model.ApplicationRevision{
|
||||
@@ -468,12 +462,13 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
Version: "revision-rollback0",
|
||||
ApplyAppConfig: `{"apiVersion":"core.oam.dev/v1beta1","kind":"Application","metadata":{"annotations":{"app.oam.dev/workflowName":"test-workflow-2-2","app.oam.dev/deployVersion":"revision-rollback1","vela.io/publish-version":"workflow-rollback1"},"name":"first-vela-app","namespace":"default"},"spec":{"components":[{"name":"express-server","properties":{"image":"crccheck/hello-world","port":8000},"traits":[{"properties":{"domain":"testsvc.example.com","http":{"/":8000}},"type":"ingress-1-20"}],"type":"webservice"}]}}`,
|
||||
Status: model.RevisionStatusComplete,
|
||||
WorkflowName: workflow.Name,
|
||||
EnvName: "rollback",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowUsecase.RollbackRecord(ctx, &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, workflow, "test-workflow-2-2", "revision-rollback0")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -489,14 +484,12 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
app.Annotations[oam.AnnotationPublishVersion] = "workflow-rollback-2"
|
||||
app.Status.Workflow.AppRevision = "workflow-rollback-2"
|
||||
err = workflowUsecase.CreateWorkflowRecord(context.TODO(), &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, app, workflow)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = workflowUsecase.RollbackRecord(ctx, &model.Application{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Name: appName,
|
||||
}, workflow, "workflow-rollback-2", "")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -527,7 +520,7 @@ metadata:
|
||||
app.oam.dev/deployVersion: "1234"
|
||||
app.oam.dev/publishVersion: "test-workflow-name-111"
|
||||
app.oam.dev/appName: "app-workflow"
|
||||
name: app-workflow-dev
|
||||
name: app-workflow
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
|
||||
@@ -61,10 +61,10 @@ var ErrApplicationNotEnv = NewBcode(404, 10013, "application not set env binding
|
||||
// ErrApplicationEnvExist application env is exist
|
||||
var ErrApplicationEnvExist = NewBcode(400, 10014, "application env is exist")
|
||||
|
||||
// ErrTraitNotExist trait is not exist
|
||||
// ErrTraitNotExist trait is not exist
|
||||
var ErrTraitNotExist = NewBcode(400, 10015, "trait is not exist")
|
||||
|
||||
// ErrTraitAlreadyExist trait is already exist
|
||||
// ErrTraitAlreadyExist trait is already exist
|
||||
var ErrTraitAlreadyExist = NewBcode(400, 10016, "trait is already exist")
|
||||
|
||||
// ErrApplicationNoReadyRevision application not have ready revision
|
||||
@@ -73,8 +73,17 @@ var ErrApplicationNoReadyRevision = NewBcode(400, 10017, "application not have r
|
||||
// ErrApplicationRevisionNotExist application revision is not exist
|
||||
var ErrApplicationRevisionNotExist = NewBcode(404, 10018, "application revision is not exist")
|
||||
|
||||
// ErrApplicationRefusedDelete The application cannot be deleted because it has been deployed
|
||||
// ErrApplicationRefusedDelete means the application cannot be deleted because it has been deployed
|
||||
var ErrApplicationRefusedDelete = NewBcode(400, 10019, "The application cannot be deleted because it has been deployed")
|
||||
|
||||
// ErrApplicationEnvRefusedDelete The application env cannot be deleted because it has been deployed
|
||||
// ErrApplicationEnvRefusedDelete means he application env cannot be deleted because it has been deployed
|
||||
var ErrApplicationEnvRefusedDelete = NewBcode(400, 10020, "The application envbinding cannot be deleted because it has been deployed")
|
||||
|
||||
// ErrInvalidWebhookToken means the webhook token is invalid
|
||||
var ErrInvalidWebhookToken = NewBcode(400, 10021, "Invalid webhook token")
|
||||
|
||||
// ErrInvalidWebhookPayloadType means the webhook payload type is invalid
|
||||
var ErrInvalidWebhookPayloadType = NewBcode(400, 10022, "Invalid webhook payload type")
|
||||
|
||||
// ErrInvalidWebhookPayloadBody means the webhook payload body is invalid
|
||||
var ErrInvalidWebhookPayloadBody = NewBcode(400, 10023, "Invalid webhook payload body")
|
||||
@@ -17,10 +17,10 @@ limitations under the License.
|
||||
package bcode
|
||||
|
||||
// ErrProjectIsExist project name is exist
|
||||
var ErrProjectIsExist = NewBcode(400, 30001, "project name is exist")
|
||||
var ErrProjectIsExist = NewBcode(400, 30001, "project name already exists")
|
||||
|
||||
// ErrProjectIsNotExist project is not exist
|
||||
var ErrProjectIsNotExist = NewBcode(404, 30002, "project is not exist")
|
||||
var ErrProjectIsNotExist = NewBcode(404, 30002, "project is not existed")
|
||||
|
||||
// ErrProjectNamespaceFail project bind namespace failure
|
||||
var ErrProjectNamespaceFail = NewBcode(400, 30003, "project bind namespace failure")
|
||||
29
pkg/apiserver/rest/utils/bcode/008_target.go
Normal file
29
pkg/apiserver/rest/utils/bcode/008_target.go
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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 bcode
|
||||
|
||||
// ErrTargetExist Target is exist
|
||||
var ErrTargetExist = NewBcode(400, 80001, "target is exist")
|
||||
|
||||
// ErrTargetNotExist Target is not exist
|
||||
var ErrTargetNotExist = NewBcode(404, 80002, "target is not exist")
|
||||
|
||||
// ErrTargetInUseCantDeleted Target being used
|
||||
var ErrTargetInUseCantDeleted = NewBcode(404, 80003, "target in use, can't be deleted")
|
||||
|
||||
// ErrTargetNamespaceAlreadyBound indicates the namespace already belongs to other target, one namespace can only belong to one target
|
||||
var ErrTargetNamespaceAlreadyBound = NewBcode(400, 80004, "the namespace specified already belongs to other target")
|
||||
35
pkg/apiserver/rest/utils/bcode/011_env.go
Normal file
35
pkg/apiserver/rest/utils/bcode/011_env.go
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
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 bcode
|
||||
|
||||
// ErrEnvAlreadyExists Env name is existed
|
||||
var ErrEnvAlreadyExists = NewBcode(400, 11001, "env name already exists")
|
||||
|
||||
// ErrEnvNotExisted means env is not existed
|
||||
var ErrEnvNotExisted = NewBcode(404, 11002, "env is not existed")
|
||||
|
||||
// ErrEnvNamespaceFail env binds namespace failure
|
||||
var ErrEnvNamespaceFail = NewBcode(400, 11003, "env bind namespace failure")
|
||||
|
||||
// ErrEnvNamespaceAlreadyBound indicates the namespace already belongs to other env
|
||||
var ErrEnvNamespaceAlreadyBound = NewBcode(400, 11004, "the namespace specified already belongs to other env")
|
||||
|
||||
// ErrDeleteEnvButAppExist reports an error when delete an Env but still has apps inside
|
||||
var ErrDeleteEnvButAppExist = NewBcode(400, 11005, "env can't be deleted as app existed inside")
|
||||
|
||||
// ErrEnvTargetConflict in one project, one target can only belong to one env
|
||||
var ErrEnvTargetConflict = NewBcode(400, 11006, "in one project, one target can only belong to one env.")
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
restful "github.com/emicklei/go-restful/v3"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
@@ -28,6 +28,10 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// Business Code of VelaUX contains 5 digits, the first 3 digits should be reversed and indicates the category of concept
|
||||
// the last two digits indicates the error number
|
||||
// For example, business code 11001 should split to 110 and 01, it means the code belongs to the 011 category env, and it's the 01 number error.
|
||||
|
||||
// ErrServer an unexpected mistake.
|
||||
var ErrServer = NewBcode(500, 500, "The service has lapsed.")
|
||||
|
||||
|
||||
@@ -1,26 +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 bcode
|
||||
|
||||
// ErrDeliveryTargetExist deliveryTarget is exist
|
||||
var ErrDeliveryTargetExist = NewBcode(400, 80001, "deliveryTarget is exist")
|
||||
|
||||
// ErrDeliveryTargetNotExist deliveryTarget is not exist
|
||||
var ErrDeliveryTargetNotExist = NewBcode(404, 80002, "deliveryTarget is not exist")
|
||||
|
||||
// ErrDeliveryTargetInUseCantDeleted deliveryTarget being used
|
||||
var ErrDeliveryTargetInUseCantDeleted = NewBcode(404, 80003, "deliveryTarget in use, can't be deleted")
|
||||
92
pkg/apiserver/rest/utils/http.go
Normal file
92
pkg/apiserver/rest/utils/http.go
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
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 utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ClientIP get client ip
|
||||
func ClientIP(r *http.Request) string {
|
||||
xForwardedFor := r.Header.Get("X-Forwarded-For")
|
||||
ip := strings.TrimSpace(strings.Split(xForwardedFor, ",")[0])
|
||||
if ip != "" {
|
||||
return ip
|
||||
}
|
||||
|
||||
ip = strings.TrimSpace(r.Header.Get("X-Real-Ip"))
|
||||
if ip != "" {
|
||||
return ip
|
||||
}
|
||||
|
||||
if ip, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)); err == nil {
|
||||
return ip
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// ResponseCapture capture response and get response info
|
||||
type ResponseCapture struct {
|
||||
http.ResponseWriter
|
||||
wroteHeader bool
|
||||
status int
|
||||
body *bytes.Buffer
|
||||
}
|
||||
|
||||
// NewResponseCapture new response capture
|
||||
func NewResponseCapture(w http.ResponseWriter) *ResponseCapture {
|
||||
return &ResponseCapture{
|
||||
ResponseWriter: w,
|
||||
wroteHeader: false,
|
||||
body: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
// Header return response writer header
|
||||
func (c ResponseCapture) Header() http.Header {
|
||||
return c.ResponseWriter.Header()
|
||||
}
|
||||
|
||||
// Write write data to response writer and body
|
||||
func (c ResponseCapture) Write(data []byte) (int, error) {
|
||||
if !c.wroteHeader {
|
||||
c.WriteHeader(http.StatusOK)
|
||||
}
|
||||
c.body.Write(data)
|
||||
return c.ResponseWriter.Write(data)
|
||||
}
|
||||
|
||||
// WriteHeader write header to response writer
|
||||
func (c *ResponseCapture) WriteHeader(statusCode int) {
|
||||
c.status = statusCode
|
||||
c.wroteHeader = true
|
||||
c.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
// Bytes return response body bytes
|
||||
func (c ResponseCapture) Bytes() []byte {
|
||||
return c.body.Bytes()
|
||||
}
|
||||
|
||||
// StatusCode return status code
|
||||
func (c ResponseCapture) StatusCode() int {
|
||||
return c.status
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
pkgaddon "github.com/oam-dev/kubevela/pkg/addon"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
@@ -122,13 +121,17 @@ func (s *addonWebService) listAddons(req *restful.Request, res *restful.Response
|
||||
return
|
||||
}
|
||||
|
||||
var addons []*pkgaddon.Meta
|
||||
var addons []*apis.AddonInfo
|
||||
|
||||
for _, d := range detailAddons {
|
||||
addons = append(addons, &d.Meta)
|
||||
addons = append(addons, &apis.AddonInfo{Meta: &d.Meta, RegistryName: d.RegistryName})
|
||||
}
|
||||
|
||||
err = res.WriteEntity(apis.ListAddonResponse{Addons: addons, Message: err.Error()})
|
||||
var message string
|
||||
if err != nil {
|
||||
message = err.Error()
|
||||
}
|
||||
err = res.WriteEntity(apis.ListAddonResponse{Addons: addons, Message: message})
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -234,7 +237,7 @@ type enabledAddonWebService struct {
|
||||
|
||||
func (s *enabledAddonWebService) GetWebService() *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(versionPrefix+"/enabled-addon").
|
||||
ws.Path(versionPrefix+"/enabled_addon").
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML).
|
||||
Doc("api for addon management")
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
restful "github.com/emicklei/go-restful/v3"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
@@ -103,6 +103,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(200, "", apis.ApplicationBase{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationBase{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/statistics").To(c.applicationStatistics).
|
||||
Doc("detail one application ").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
@@ -122,6 +123,25 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationBase{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/triggers").To(c.createApplicationTrigger).
|
||||
Doc("create one application trigger").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Reads(apis.CreateApplicationTriggerRequest{}).
|
||||
Returns(200, "", apis.ApplicationTriggerBase{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationTriggerBase{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/triggers").To(c.listApplicationTriggers).
|
||||
Doc("list application triggers").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "", apis.ListApplicationTriggerResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes([]*apis.ApplicationTriggerBase{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/template").To(c.publishApplicationTemplate).
|
||||
Doc("create one application template").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
@@ -306,7 +326,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Reads(apis.CreateApplicationEnvRequest{}).
|
||||
Reads(apis.CreateApplicationEnvbindingRequest{}).
|
||||
Returns(200, "", apis.EnvBinding{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
@@ -318,7 +338,7 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Filter(c.envCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("envName", "identifier of the envBinding ").DataType("string")).
|
||||
Reads(apis.PutApplicationEnvRequest{}).
|
||||
Reads(apis.PutApplicationEnvBindingRequest{}).
|
||||
Returns(200, "", apis.EnvBinding{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.EnvBinding{}))
|
||||
@@ -504,8 +524,9 @@ func (c *applicationWebService) createApplication(req *restful.Request, res *res
|
||||
}
|
||||
|
||||
func (c *applicationWebService) listApplications(req *restful.Request, res *restful.Response) {
|
||||
apps, err := c.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicatioOptions{
|
||||
apps, err := c.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicationOptions{
|
||||
Project: req.QueryParameter("project"),
|
||||
Env: req.QueryParameter("env"),
|
||||
TargetName: req.QueryParameter("targetName"),
|
||||
Query: req.QueryParameter("query"),
|
||||
})
|
||||
@@ -532,6 +553,37 @@ func (c *applicationWebService) detailApplication(req *restful.Request, res *res
|
||||
}
|
||||
}
|
||||
|
||||
func (c *applicationWebService) createApplicationTrigger(req *restful.Request, res *restful.Response) {
|
||||
var createReq apis.CreateApplicationTriggerRequest
|
||||
if err := req.ReadEntity(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
|
||||
base, err := c.applicationUsecase.CreateApplicationTrigger(req.Request.Context(), app, createReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(base); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *applicationWebService) listApplicationTriggers(req *restful.Request, res *restful.Response) {
|
||||
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
|
||||
triggers, err := c.applicationUsecase.ListApplicationTriggers(req.Request.Context(), app)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(apis.ListApplicationTriggerResponse{Triggers: triggers}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *applicationWebService) publishApplicationTemplate(req *restful.Request, res *restful.Response) {
|
||||
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
|
||||
base, err := c.applicationUsecase.PublishApplicationTemplate(req.Request.Context(), app)
|
||||
@@ -872,7 +924,7 @@ func (c *applicationWebService) detailApplicationRevision(req *restful.Request,
|
||||
func (c *applicationWebService) updateApplicationEnv(req *restful.Request, res *restful.Response) {
|
||||
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
|
||||
// Verify the validity of parameters
|
||||
var updateReq apis.PutApplicationEnvRequest
|
||||
var updateReq apis.PutApplicationEnvBindingRequest
|
||||
if err := req.ReadEntity(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -908,7 +960,7 @@ func (c *applicationWebService) listApplicationEnvs(req *restful.Request, res *r
|
||||
func (c *applicationWebService) createApplicationEnv(req *restful.Request, res *restful.Response) {
|
||||
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
|
||||
// Verify the validity of parameters
|
||||
var createReq apis.CreateApplicationEnvRequest
|
||||
var createReq apis.CreateApplicationEnvbindingRequest
|
||||
if err := req.ReadEntity(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
|
||||
@@ -1,216 +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 webservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
restful "github.com/emicklei/go-restful/v3"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
// NewDeliveryTargetWebService new deliveryTarget webservice
|
||||
func NewDeliveryTargetWebService(deliveryTargetUsecase usecase.DeliveryTargetUsecase, applicationUsecase usecase.ApplicationUsecase) WebService {
|
||||
return &DeliveryTargetWebService{
|
||||
deliveryTargetUsecase: deliveryTargetUsecase,
|
||||
applicationUsecase: applicationUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
// DeliveryTargetWebService delivery target web service
|
||||
type DeliveryTargetWebService struct {
|
||||
deliveryTargetUsecase usecase.DeliveryTargetUsecase
|
||||
applicationUsecase usecase.ApplicationUsecase
|
||||
}
|
||||
|
||||
// GetWebService get web service
|
||||
func (dt *DeliveryTargetWebService) GetWebService() *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(versionPrefix+"/targets").
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML).
|
||||
Doc("api for deliveryTarget manage")
|
||||
|
||||
tags := []string{"deliveryTarget"}
|
||||
|
||||
ws.Route(ws.GET("/").To(dt.listDeliveryTargets).
|
||||
Doc("list deliveryTarget").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("project", "Query the target belong to project").DataType("string")).
|
||||
Param(ws.QueryParameter("page", "Page for paging").DataType("integer")).
|
||||
Param(ws.QueryParameter("pageSize", "PageSize for paging").DataType("integer")).
|
||||
Returns(200, "", apis.ListTargetResponse{}).
|
||||
Writes(apis.ListTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.POST("/").To(dt.createDeliveryTarget).
|
||||
Doc("create deliveryTarget").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateDeliveryTargetRequest{}).
|
||||
Returns(200, "create success", apis.DetailDeliveryTargetResponse{}).
|
||||
Returns(400, "create failure", bcode.Bcode{}).
|
||||
Writes(apis.DetailDeliveryTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.GET("/{name}").To(dt.detailDeliveryTarget).
|
||||
Doc("detail deliveryTarget").
|
||||
Param(ws.PathParameter("name", "identifier of the deliveryTarget.").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.deliveryTargetCheckFilter).
|
||||
Returns(200, "create success", apis.DetailDeliveryTargetResponse{}).
|
||||
Writes(apis.DetailDeliveryTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.PUT("/{name}").To(dt.updateDeliveryTarget).
|
||||
Doc("update application DeliveryTarget config").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.deliveryTargetCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the deliveryTarget").DataType("string")).
|
||||
Reads(apis.UpdateDeliveryTargetRequest{}).
|
||||
Returns(200, "", apis.DetailDeliveryTargetResponse{}).
|
||||
Writes(apis.DetailDeliveryTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}").To(dt.deleteDeliveryTarget).
|
||||
Doc("deletet DeliveryTarget").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.deliveryTargetCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the deliveryTarget").DataType("string")).
|
||||
Returns(200, "", apis.EmptyResponse{}).
|
||||
Writes(apis.EmptyResponse{}).Do(returns200, returns500))
|
||||
|
||||
return ws
|
||||
}
|
||||
|
||||
func (dt *DeliveryTargetWebService) createDeliveryTarget(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var createReq apis.CreateDeliveryTargetRequest
|
||||
if err := req.ReadEntity(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
deliveryTargetDetail, err := dt.deliveryTargetUsecase.CreateDeliveryTarget(req.Request.Context(), createReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("create delivery-target failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(deliveryTargetDetail); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *DeliveryTargetWebService) deliveryTargetCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
|
||||
deliveryTarget, err := dt.deliveryTargetUsecase.GetDeliveryTarget(req.Request.Context(), req.PathParameter("name"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
req.Request = req.Request.WithContext(context.WithValue(req.Request.Context(), &apis.CtxKeyDeliveryTarget, deliveryTarget))
|
||||
chain.ProcessFilter(req, res)
|
||||
}
|
||||
|
||||
func (dt *DeliveryTargetWebService) detailDeliveryTarget(req *restful.Request, res *restful.Response) {
|
||||
deliveryTarget := req.Request.Context().Value(&apis.CtxKeyDeliveryTarget).(*model.DeliveryTarget)
|
||||
detail, err := dt.deliveryTargetUsecase.DetailDeliveryTarget(req.Request.Context(), deliveryTarget)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(detail); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *DeliveryTargetWebService) updateDeliveryTarget(req *restful.Request, res *restful.Response) {
|
||||
deliveryTarget := req.Request.Context().Value(&apis.CtxKeyDeliveryTarget).(*model.DeliveryTarget)
|
||||
// Verify the validity of parameters
|
||||
var updateReq apis.UpdateDeliveryTargetRequest
|
||||
if err := req.ReadEntity(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
detail, err := dt.deliveryTargetUsecase.UpdateDeliveryTarget(req.Request.Context(), deliveryTarget, updateReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(detail); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *DeliveryTargetWebService) deleteDeliveryTarget(req *restful.Request, res *restful.Response) {
|
||||
deliveryTargetName := req.PathParameter("name")
|
||||
// deliveryTarget in use, can't be deleted
|
||||
applications, err := dt.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicatioOptions{TargetName: deliveryTargetName})
|
||||
if err != nil {
|
||||
if !errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if applications != nil {
|
||||
bcode.ReturnError(req, res, bcode.ErrDeliveryTargetInUseCantDeleted)
|
||||
return
|
||||
}
|
||||
if err := dt.deliveryTargetUsecase.DeleteDeliveryTarget(req.Request.Context(), deliveryTargetName); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *DeliveryTargetWebService) listDeliveryTargets(req *restful.Request, res *restful.Response) {
|
||||
page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
deliveryTargets, err := dt.deliveryTargetUsecase.ListDeliveryTargets(req.Request.Context(), page, pageSize, req.QueryParameter("project"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(deliveryTargets); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
173
pkg/apiserver/rest/webservice/env.go
Normal file
173
pkg/apiserver/rest/webservice/env.go
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
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 webservice
|
||||
|
||||
import (
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
type envWebService struct {
|
||||
envUsecase usecase.EnvUsecase
|
||||
appUsecase usecase.ApplicationUsecase
|
||||
}
|
||||
|
||||
// NewEnvWebService new env webservice
|
||||
func NewEnvWebService(envUsecase usecase.EnvUsecase, appUseCase usecase.ApplicationUsecase) WebService {
|
||||
return &envWebService{envUsecase: envUsecase, appUsecase: appUseCase}
|
||||
}
|
||||
|
||||
func (n *envWebService) GetWebService() *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(versionPrefix+"/envs").
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML).
|
||||
Doc("api for env management")
|
||||
|
||||
tags := []string{"env"}
|
||||
|
||||
ws.Route(ws.GET("/").To(n.list).
|
||||
Doc("list all envs").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.ListEnvResponse{}).
|
||||
Writes(apis.ListEnvResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/").To(n.create).
|
||||
Doc("create an env").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateEnvRequest{}).
|
||||
Returns(200, "", apis.Env{}).
|
||||
Writes(apis.Env{}))
|
||||
|
||||
ws.Route(ws.PUT("/{name}").To(n.update).
|
||||
Doc("update an env").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateEnvRequest{}).
|
||||
Returns(200, "", apis.Env{}).
|
||||
Writes(apis.Env{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}").To(n.delete).
|
||||
Doc("delete one env").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "", apis.EmptyResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
return ws
|
||||
}
|
||||
|
||||
func (n *envWebService) list(req *restful.Request, res *restful.Response) {
|
||||
page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
project := req.QueryParameter("project")
|
||||
envs, err := n.envUsecase.ListEnvs(req.Request.Context(), page, pageSize, apis.ListEnvOptions{Project: project})
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(apis.ListEnvResponse{Envs: envs}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// it will prevent the deletion if there's still application in it.
|
||||
func (n *envWebService) delete(req *restful.Request, res *restful.Response) {
|
||||
envname := req.PathParameter("name")
|
||||
|
||||
ctx := req.Request.Context()
|
||||
lists, err := n.appUsecase.ListApplications(ctx, apis.ListApplicationOptions{Env: envname})
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if len(lists) > 0 {
|
||||
log.Logger.Infof("detected %d applications in this env, the first is %s", len(lists), lists[0].Name)
|
||||
bcode.ReturnError(req, res, bcode.ErrDeleteEnvButAppExist)
|
||||
return
|
||||
}
|
||||
|
||||
err = n.envUsecase.DeleteEnv(ctx, envname)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err = res.WriteEntity(apis.EmptyResponse{}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *envWebService) create(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var createReq apis.CreateEnvRequest
|
||||
if err := req.ReadEntity(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
env, err := n.envUsecase.CreateEnv(req.Request.Context(), createReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("create application failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(env); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *envWebService) update(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var updateReq apis.UpdateEnvRequest
|
||||
if err := req.ReadEntity(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
env, err := n.envUsecase.UpdateEnv(req.Request.Context(), req.PathParameter("name"), updateReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(env); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
215
pkg/apiserver/rest/webservice/target.go
Normal file
215
pkg/apiserver/rest/webservice/target.go
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
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 webservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
// NewTargetWebService new Target webservice
|
||||
func NewTargetWebService(targetUsecase usecase.TargetUsecase, applicationUsecase usecase.ApplicationUsecase) WebService {
|
||||
return &TargetWebService{
|
||||
TargetUsecase: targetUsecase,
|
||||
applicationUsecase: applicationUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
// TargetWebService target web service
|
||||
type TargetWebService struct {
|
||||
TargetUsecase usecase.TargetUsecase
|
||||
applicationUsecase usecase.ApplicationUsecase
|
||||
}
|
||||
|
||||
// GetWebService get web service
|
||||
func (dt *TargetWebService) GetWebService() *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(versionPrefix+"/targets").
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML).
|
||||
Doc("api for Target manage")
|
||||
|
||||
tags := []string{"Target"}
|
||||
|
||||
ws.Route(ws.GET("/").To(dt.listTargets).
|
||||
Doc("list Target").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("page", "Page for paging").DataType("integer")).
|
||||
Param(ws.QueryParameter("pageSize", "PageSize for paging").DataType("integer")).
|
||||
Returns(200, "", apis.ListTargetResponse{}).
|
||||
Writes(apis.ListTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.POST("/").To(dt.createTarget).
|
||||
Doc("create Target").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateTargetRequest{}).
|
||||
Returns(200, "create success", apis.DetailTargetResponse{}).
|
||||
Returns(400, "create failure", bcode.Bcode{}).
|
||||
Writes(apis.DetailTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.GET("/{name}").To(dt.detailTarget).
|
||||
Doc("detail Target").
|
||||
Param(ws.PathParameter("name", "identifier of the Target.").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.targetCheckFilter).
|
||||
Returns(200, "create success", apis.DetailTargetResponse{}).
|
||||
Writes(apis.DetailTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.PUT("/{name}").To(dt.updateTarget).
|
||||
Doc("update application Target config").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.targetCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the Target").DataType("string")).
|
||||
Reads(apis.UpdateTargetRequest{}).
|
||||
Returns(200, "", apis.DetailTargetResponse{}).
|
||||
Writes(apis.DetailTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}").To(dt.deleteTarget).
|
||||
Doc("deletet Target").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.targetCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the Target").DataType("string")).
|
||||
Returns(200, "", apis.EmptyResponse{}).
|
||||
Writes(apis.EmptyResponse{}).Do(returns200, returns500))
|
||||
|
||||
return ws
|
||||
}
|
||||
|
||||
func (dt *TargetWebService) createTarget(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var createReq apis.CreateTargetRequest
|
||||
if err := req.ReadEntity(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
TargetDetail, err := dt.TargetUsecase.CreateTarget(req.Request.Context(), createReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("create -target failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(TargetDetail); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *TargetWebService) targetCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
|
||||
Target, err := dt.TargetUsecase.GetTarget(req.Request.Context(), req.PathParameter("name"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
req.Request = req.Request.WithContext(context.WithValue(req.Request.Context(), &apis.CtxKeyTarget, Target))
|
||||
chain.ProcessFilter(req, res)
|
||||
}
|
||||
|
||||
func (dt *TargetWebService) detailTarget(req *restful.Request, res *restful.Response) {
|
||||
Target := req.Request.Context().Value(&apis.CtxKeyTarget).(*model.Target)
|
||||
detail, err := dt.TargetUsecase.DetailTarget(req.Request.Context(), Target)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(detail); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *TargetWebService) updateTarget(req *restful.Request, res *restful.Response) {
|
||||
Target := req.Request.Context().Value(&apis.CtxKeyTarget).(*model.Target)
|
||||
// Verify the validity of parameters
|
||||
var updateReq apis.UpdateTargetRequest
|
||||
if err := req.ReadEntity(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
detail, err := dt.TargetUsecase.UpdateTarget(req.Request.Context(), Target, updateReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(detail); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *TargetWebService) deleteTarget(req *restful.Request, res *restful.Response) {
|
||||
TargetName := req.PathParameter("name")
|
||||
// Target in use, can't be deleted
|
||||
applications, err := dt.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicationOptions{TargetName: TargetName})
|
||||
if err != nil {
|
||||
if !errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if applications != nil {
|
||||
bcode.ReturnError(req, res, bcode.ErrTargetInUseCantDeleted)
|
||||
return
|
||||
}
|
||||
if err := dt.TargetUsecase.DeleteTarget(req.Request.Context(), TargetName); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *TargetWebService) listTargets(req *restful.Request, res *restful.Response) {
|
||||
page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
Targets, err := dt.TargetUsecase.ListTargets(req.Request.Context(), page, pageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(Targets); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
71
pkg/apiserver/rest/webservice/webhook.go
Normal file
71
pkg/apiserver/rest/webservice/webhook.go
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
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 webservice
|
||||
|
||||
import (
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
type webhookWebService struct {
|
||||
webhookUsecase usecase.WebhookUsecase
|
||||
applicationUsecase usecase.ApplicationUsecase
|
||||
}
|
||||
|
||||
// NewWebhookWebService new application manage webservice
|
||||
func NewWebhookWebService(webhookUsecase usecase.WebhookUsecase, applicationUsecase usecase.ApplicationUsecase) WebService {
|
||||
return &webhookWebService{
|
||||
webhookUsecase: webhookUsecase,
|
||||
applicationUsecase: applicationUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *webhookWebService) GetWebService() *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(versionPrefix+"/webhook").
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML).
|
||||
Doc("api for webhook manage")
|
||||
|
||||
tags := []string{"webhook"}
|
||||
|
||||
ws.Route(ws.POST("/{token}").To(c.handleApplicationWebhook).
|
||||
Doc("handle application webhook request").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Reads(apis.HandleApplicationWebhookRequest{}).
|
||||
Returns(200, "", apis.ApplicationDeployResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationDeployResponse{}))
|
||||
return ws
|
||||
}
|
||||
|
||||
func (c *webhookWebService) handleApplicationWebhook(req *restful.Request, res *restful.Response) {
|
||||
base, err := c.webhookUsecase.HandleApplicationWebhook(req.Request.Context(), req.PathParameter("token"), req)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(base); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -34,16 +34,16 @@ type WebService interface {
|
||||
GetWebService() *restful.WebService
|
||||
}
|
||||
|
||||
var registedWebService []WebService
|
||||
var registeredWebService []WebService
|
||||
|
||||
// RegistWebService regist webservice
|
||||
func RegistWebService(ws WebService) {
|
||||
registedWebService = append(registedWebService, ws)
|
||||
// RegisterWebService regist webservice
|
||||
func RegisterWebService(ws WebService) {
|
||||
registeredWebService = append(registeredWebService, ws)
|
||||
}
|
||||
|
||||
// GetRegistedWebService return registedWebService
|
||||
func GetRegistedWebService() []WebService {
|
||||
return registedWebService
|
||||
// GetRegisteredWebService return registeredWebService
|
||||
func GetRegisteredWebService() []WebService {
|
||||
return registeredWebService
|
||||
}
|
||||
|
||||
func noop(req *restful.Request, resp *restful.Response) {}
|
||||
@@ -60,24 +60,36 @@ func returns500(b *restful.RouteBuilder) {
|
||||
// It can be implemented using the idea of dependency injection.
|
||||
func Init(ds datastore.DataStore, addonCacheTime time.Duration) {
|
||||
clusterUsecase := usecase.NewClusterUsecase(ds)
|
||||
workflowUsecase := usecase.NewWorkflowUsecase(ds)
|
||||
envUsecase := usecase.NewEnvUsecase(ds)
|
||||
workflowUsecase := usecase.NewWorkflowUsecase(ds, envUsecase)
|
||||
projectUsecase := usecase.NewProjectUsecase(ds)
|
||||
deliveryTargetUsecase := usecase.NewDeliveryTargetUsecase(ds, projectUsecase)
|
||||
targetUsecase := usecase.NewTargetUsecase(ds)
|
||||
oamApplicationUsecase := usecase.NewOAMApplicationUsecase()
|
||||
velaQLUsecase := usecase.NewVelaQLUsecase()
|
||||
definitionUsecase := usecase.NewDefinitionUsecase()
|
||||
addonUsecase := usecase.NewAddonUsecase(addonCacheTime)
|
||||
envBindingUsecase := usecase.NewEnvBindingUsecase(ds, workflowUsecase, definitionUsecase)
|
||||
applicationUsecase := usecase.NewApplicationUsecase(ds, workflowUsecase, envBindingUsecase, deliveryTargetUsecase, definitionUsecase, projectUsecase)
|
||||
RegistWebService(NewClusterWebService(clusterUsecase))
|
||||
RegistWebService(NewApplicationWebService(applicationUsecase, envBindingUsecase, workflowUsecase))
|
||||
RegistWebService(NewProjectWebService(projectUsecase))
|
||||
RegistWebService(NewDefinitionWebservice(definitionUsecase))
|
||||
RegistWebService(NewAddonWebService(addonUsecase))
|
||||
RegistWebService(NewEnabledAddonWebService(addonUsecase))
|
||||
RegistWebService(NewAddonRegistryWebService(addonUsecase))
|
||||
RegistWebService(NewOAMApplication(oamApplicationUsecase))
|
||||
RegistWebService(&policyDefinitionWebservice{})
|
||||
RegistWebService(NewDeliveryTargetWebService(deliveryTargetUsecase, applicationUsecase))
|
||||
RegistWebService(NewVelaQLWebService(velaQLUsecase))
|
||||
envBindingUsecase := usecase.NewEnvBindingUsecase(ds, workflowUsecase, definitionUsecase, envUsecase)
|
||||
applicationUsecase := usecase.NewApplicationUsecase(ds, workflowUsecase, envBindingUsecase, envUsecase, targetUsecase, definitionUsecase, projectUsecase)
|
||||
webhookUsecase := usecase.NewWebhookUsecase(ds, applicationUsecase)
|
||||
|
||||
// init for default values
|
||||
|
||||
// Application
|
||||
RegisterWebService(NewApplicationWebService(applicationUsecase, envBindingUsecase, workflowUsecase))
|
||||
RegisterWebService(NewProjectWebService(projectUsecase))
|
||||
RegisterWebService(NewEnvWebService(envUsecase, applicationUsecase))
|
||||
|
||||
// Extension
|
||||
RegisterWebService(NewDefinitionWebservice(definitionUsecase))
|
||||
RegisterWebService(NewAddonWebService(addonUsecase))
|
||||
RegisterWebService(NewEnabledAddonWebService(addonUsecase))
|
||||
RegisterWebService(NewAddonRegistryWebService(addonUsecase))
|
||||
|
||||
// Resources
|
||||
RegisterWebService(NewClusterWebService(clusterUsecase))
|
||||
RegisterWebService(NewOAMApplication(oamApplicationUsecase))
|
||||
RegisterWebService(&policyDefinitionWebservice{})
|
||||
RegisterWebService(NewTargetWebService(targetUsecase, applicationUsecase))
|
||||
RegisterWebService(NewVelaQLWebService(velaQLUsecase))
|
||||
RegisterWebService(NewWebhookWebService(webhookUsecase, applicationUsecase))
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ func GetMutableClusterSecret(ctx context.Context, c client.Client, clusterName s
|
||||
for _, env := range status.Envs {
|
||||
for _, placement := range env.Placements {
|
||||
if placement.Cluster == clusterName {
|
||||
errs.Append(fmt.Errorf("application %s/%s (env: %s) is currently using cluster %s", app.Namespace, app.Name, env.Env, clusterName))
|
||||
errs = append(errs, fmt.Errorf("application %s/%s (env: %s) is currently using cluster %s", app.Namespace, app.Name, env.Env, clusterName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user