mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-23 06:14:30 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbef61d076 | ||
|
|
52f9b7e691 | ||
|
|
e721449c46 | ||
|
|
ab998ce3f4 | ||
|
|
bcc978380f | ||
|
|
b9f9f7f3f9 | ||
|
|
18ceb467ed | ||
|
|
06eb8f055d | ||
|
|
9dec98fbba | ||
|
|
42e7f04267 | ||
|
|
188e453f8a | ||
|
|
c34cd657e8 |
2
.github/workflows/apiserver-test.yaml
vendored
2
.github/workflows/apiserver-test.yaml
vendored
@@ -15,7 +15,7 @@ on:
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.16'
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
|
||||
|
||||
2
.github/workflows/e2e-multicluster-test.yml
vendored
2
.github/workflows/e2e-multicluster-test.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.16'
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
|
||||
|
||||
2
.github/workflows/e2e-rollout-test.yml
vendored
2
.github/workflows/e2e-rollout-test.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.16'
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
|
||||
|
||||
2
.github/workflows/e2e-test.yml
vendored
2
.github/workflows/e2e-test.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.16'
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
|
||||
|
||||
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.16'
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: 1.17
|
||||
- name: Get release
|
||||
id: get_release
|
||||
uses: bruceadams/get-release@v1.2.2
|
||||
|
||||
4
.github/workflows/sync-api.yml
vendored
4
.github/workflows/sync-api.yml
vendored
@@ -11,10 +11,10 @@ jobs:
|
||||
sync-core-api:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Set up Go 1.16
|
||||
- name: Set up Go 1.17
|
||||
uses: actions/setup-go@v1
|
||||
env:
|
||||
GO_VERSION: '1.16'
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
2
.github/workflows/unit-test.yml
vendored
2
.github/workflows/unit-test.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.16'
|
||||
GO_VERSION: '1.17'
|
||||
GOLANGCI_VERSION: 'v1.38'
|
||||
KIND_VERSION: 'v0.7.0'
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ spec:
|
||||
arch: amd64
|
||||
{{addURIAndSha "https://github.com/oam-dev/kubevela/releases/download/{{ .TagName }}/kubectl-vela-{{ .TagName }}-windows-amd64.zip" .TagName }}
|
||||
files:
|
||||
- from: "*/kubectl-vela.exe"
|
||||
to: "."
|
||||
- from: "*/kubectl-vela"
|
||||
to: "kubectl-vela.exe"
|
||||
- from: "*/LICENSE"
|
||||
to: "."
|
||||
bin: "kubectl-vela.exe"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ARG BASE_IMAGE="alpine:latest"
|
||||
ARG BASE_IMAGE
|
||||
# Build the manager binary
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.16-alpine as builder
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.17-alpine as builder
|
||||
|
||||
WORKDIR /workspace
|
||||
# Copy the Go Modules manifests
|
||||
@@ -34,9 +34,9 @@ RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
|
||||
# You can replace distroless as minimal base image to package the manager binary
|
||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||
# Overwrite `BASE_IMAGE` by passing `--build-arg=BASE_IMAGE=gcr.io/distroless/static:nonroot`
|
||||
FROM ${BASE_IMAGE:-alpine:latest}
|
||||
FROM ${BASE_IMAGE:-alpine:3.15}
|
||||
# This is required by daemon connnecting with cri
|
||||
RUN apk add --no-cache ca-certificates bash
|
||||
RUN apk add --no-cache ca-certificates bash expat
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ARG BASE_IMAGE="alpine:latest"
|
||||
ARG BASE_IMAGE
|
||||
# Build the manager binary
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.16-alpine as builder
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.17-alpine as builder
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-https://goproxy.cn}
|
||||
WORKDIR /workspace
|
||||
@@ -32,9 +32,9 @@ RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
|
||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||
# Overwrite `BASE_IMAGE` by passing `--build-arg=BASE_IMAGE=gcr.io/distroless/static:nonroot`
|
||||
|
||||
FROM ${BASE_IMAGE:-alpine:latest}
|
||||
FROM ${BASE_IMAGE:-alpine:3.15}
|
||||
# This is required by daemon connnecting with cri
|
||||
RUN apk add --no-cache ca-certificates bash
|
||||
RUN apk add --no-cache ca-certificates bash expat
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
ARG BASE_IMAGE
|
||||
# Build the manager binary
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.16-alpine as builder
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.17-alpine as builder
|
||||
|
||||
WORKDIR /workspace
|
||||
# Copy the Go Modules manifests
|
||||
@@ -24,8 +25,8 @@ ARG VERSION
|
||||
ARG GITVERSION
|
||||
|
||||
RUN apk add gcc musl-dev libc-dev ;\
|
||||
GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
|
||||
go test -c -o manager-${TARGETARCH} -cover -covermode=atomic -coverpkg ./... .
|
||||
GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
|
||||
go test -c -o manager-${TARGETARCH} -cover -covermode=atomic -coverpkg ./... .
|
||||
|
||||
RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
|
||||
go build -a -ldflags "-s -w -X github.com/oam-dev/kubevela/version.VelaVersion=${VERSION:-undefined} -X github.com/oam-dev/kubevela/version.GitRevision=${GITVERSION:-undefined}" \
|
||||
@@ -35,10 +36,10 @@ RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} \
|
||||
# You can replace distroless as minimal base image to package the manager binary
|
||||
# Refer to https://github.com/GoogleContainerTools/distroless for more details
|
||||
# Overwrite `BASE_IMAGE` by passing `--build-arg=BASE_IMAGE=gcr.io/distroless/static:nonroot`
|
||||
ARG BASE_IMAGE
|
||||
FROM ${BASE_IMAGE:-alpine:latest}
|
||||
|
||||
FROM ${BASE_IMAGE:-alpine:3.15}
|
||||
# This is required by daemon connnecting with cri
|
||||
RUN apk add --no-cache ca-certificates bash
|
||||
RUN apk add --no-cache ca-certificates bash expat
|
||||
|
||||
WORKDIR /
|
||||
|
||||
|
||||
5
Makefile
5
Makefile
@@ -9,9 +9,12 @@ include makefiles/e2e.mk
|
||||
all: build
|
||||
|
||||
# Run tests
|
||||
test: vet lint staticcheck unit-test-core
|
||||
test: vet lint staticcheck unit-test-core test-cli-gen
|
||||
@$(OK) unit-tests pass
|
||||
|
||||
test-cli-gen:
|
||||
mkdir -p ./bin/doc
|
||||
go run ./hack/docgen/gen.go ./bin/doc
|
||||
unit-test-core:
|
||||
go test -coverprofile=coverage.txt $(shell go list ./pkg/... ./cmd/... ./apis/... | grep -v apiserver)
|
||||
go test $(shell go list ./references/... | grep -v apiserver)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
|
||||
@@ -2,7 +2,7 @@ apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: vela-addon-registry
|
||||
namespace: vela-system
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
data:
|
||||
registries: '{
|
||||
"KubeVela":{
|
||||
@@ -13,4 +13,4 @@ data:
|
||||
"path": ""
|
||||
}
|
||||
}
|
||||
}'
|
||||
}'
|
||||
|
||||
@@ -5,6 +5,7 @@ metadata:
|
||||
helm.sh/hook: test-success
|
||||
helm.sh/hook-delete-policy: hook-succeeded
|
||||
name: helm-test-vela-app
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
spec:
|
||||
components:
|
||||
- name: helm-test-express-server
|
||||
@@ -23,6 +24,7 @@ apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ .Release.Name }}-application-test"
|
||||
namespace: {{.Values.systemDefinitionNamespace}}
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
helm.sh/hook-delete-policy: hook-succeeded
|
||||
@@ -42,16 +44,16 @@ spec:
|
||||
echo "Waiting application is ready..."
|
||||
|
||||
echo "waiting for application being Ready"
|
||||
kubectl -n vela-system wait --for=condition=Ready applications.core.oam.dev helm-test-vela-app --timeout=3m
|
||||
kubectl -n {{.Values.systemDefinitionNamespace}} wait --for=condition=Ready applications.core.oam.dev helm-test-vela-app --timeout=3m
|
||||
echo "application is Ready"
|
||||
|
||||
# wait for deploy being created
|
||||
echo "waiting for deployment being available"
|
||||
kubectl -n vela-system wait --for=condition=available deployments helm-test-express-server --timeout 3m
|
||||
kubectl -n {{.Values.systemDefinitionNamespace}} wait --for=condition=available deployments helm-test-express-server --timeout 3m
|
||||
echo "deployment being available"
|
||||
|
||||
# wait for ingress being created
|
||||
while ! [ `kubectl -n vela-system get ing helm-test-express-server | grep -v NAME | wc -l` = 1 ]; do
|
||||
while ! [ `kubectl -n {{.Values.systemDefinitionNamespace}} get ing helm-test-express-server | grep -v NAME | wc -l` = 1 ]; do
|
||||
echo "waiting for ingress being created"
|
||||
sleep 1
|
||||
done
|
||||
@@ -59,4 +61,4 @@ spec:
|
||||
|
||||
|
||||
echo "Application and its components are created"
|
||||
restartPolicy: Never
|
||||
restartPolicy: Never
|
||||
|
||||
@@ -117,7 +117,7 @@ multicluster:
|
||||
image:
|
||||
repository: oamdev/cluster-gateway
|
||||
tag: v1.1.7
|
||||
pullPolicy: Always
|
||||
pullPolicy: IfNotPresent
|
||||
resources:
|
||||
limits:
|
||||
cpu: 100m
|
||||
|
||||
@@ -46,6 +46,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcekeeper"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/system"
|
||||
oamwebhook "github.com/oam-dev/kubevela/pkg/webhook/core.oam.dev"
|
||||
@@ -131,6 +132,7 @@ func main() {
|
||||
flag.BoolVar(&enableClusterGateway, "enable-cluster-gateway", false, "Enable cluster-gateway to use multicluster, disabled by default.")
|
||||
flag.BoolVar(&controllerArgs.EnableCompatibility, "enable-asi-compatibility", false, "enable compatibility for asi")
|
||||
standardcontroller.AddOptimizeFlags()
|
||||
flag.IntVar(&resourcekeeper.MaxDispatchConcurrent, "max-dispatch-concurrent", 10, "Set the max dispatch concurrent number, default is 10")
|
||||
|
||||
flag.Parse()
|
||||
// setup logging
|
||||
|
||||
@@ -4,7 +4,7 @@ This guide helps you get started developing KubeVela.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. Golang version 1.16+
|
||||
1. Golang version 1.17+
|
||||
2. Kubernetes version v1.18+ with `~/.kube/config` configured.
|
||||
3. ginkgo 1.14.0+ (just for [E2E test](./developer-guide.md#e2e-test))
|
||||
4. golangci-lint 1.38.0+, it will install automatically if you run `make`, you can [install it manually](https://golangci-lint.run/usage/install/#local-installation) if the installation is too slow.
|
||||
@@ -15,6 +15,7 @@ This guide helps you get started developing KubeVela.
|
||||
<summary>Install Kubebuilder manually</summary>
|
||||
|
||||
linux:
|
||||
|
||||
```
|
||||
wget https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-1.21.2-linux-amd64.tar.gz
|
||||
tar -zxvf kubebuilder-tools-1.21.2-linux-amd64.tar.gz
|
||||
@@ -23,6 +24,7 @@ sudo mv kubebuilder/bin/* /usr/local/kubebuilder/bin
|
||||
```
|
||||
|
||||
macOS:
|
||||
|
||||
```
|
||||
wget https://storage.googleapis.com/kubebuilder-tools/kubebuilder-tools-1.21.2-darwin-amd64.tar.gz
|
||||
tar -zxvf kubebuilder-tools-1.21.2-darwin-amd64.tar.gz
|
||||
@@ -30,14 +32,15 @@ mkdir -p /usr/local/kubebuilder/bin
|
||||
sudo mv kubebuilder/bin/* /usr/local/kubebuilder/bin
|
||||
```
|
||||
|
||||
For other OS or system architecture, please refer to https://storage.googleapis.com/kubebuilder-tools/
|
||||
For other OS or system architecture, please refer to https://storage.googleapis.com/kubebuilder-tools/
|
||||
|
||||
</details>
|
||||
|
||||
You may also be interested with KubeVela's [design](https://github.com/oam-dev/kubevela/tree/master/design/vela-core) before diving into its code.
|
||||
|
||||
## Build
|
||||
|
||||
* Clone this project
|
||||
- Clone this project
|
||||
|
||||
```shell script
|
||||
git clone git@github.com:oam-dev/kubevela.git
|
||||
@@ -50,7 +53,7 @@ KubeVela includes two parts, `vela core` and `vela cli`.
|
||||
|
||||
For local development, we probably need to build both of them.
|
||||
|
||||
* Build Vela CLI
|
||||
- Build Vela CLI
|
||||
|
||||
```shell script
|
||||
make
|
||||
@@ -58,7 +61,7 @@ make
|
||||
|
||||
After the vela cli built successfully, `make` command will create `vela` binary to `bin/` under the project.
|
||||
|
||||
* Configure `vela` binary to System PATH
|
||||
- Configure `vela` binary to System PATH
|
||||
|
||||
```shell script
|
||||
export PATH=$PATH:/your/path/to/project/kubevela/bin
|
||||
@@ -66,13 +69,13 @@ export PATH=$PATH:/your/path/to/project/kubevela/bin
|
||||
|
||||
Then you can use `vela` command directly.
|
||||
|
||||
* Build Vela Core
|
||||
- Build Vela Core
|
||||
|
||||
```shell script
|
||||
make manager
|
||||
```
|
||||
|
||||
* Run Vela Core
|
||||
- Run Vela Core
|
||||
|
||||
Firstly make sure your cluster has CRDs, below is the command that can help install all CRDs.
|
||||
|
||||
@@ -82,11 +85,13 @@ make core-install
|
||||
|
||||
To ensure you have created vela-system namespace and install definitions of necessary module.
|
||||
you can run the command:
|
||||
|
||||
```shell script
|
||||
make def-install
|
||||
```
|
||||
|
||||
And then run locally:
|
||||
|
||||
```shell script
|
||||
make core-run
|
||||
```
|
||||
@@ -182,8 +187,7 @@ mv ~/.kube/config.save ~/.kube/config
|
||||
make e2e-apiserver-test
|
||||
```
|
||||
|
||||
|
||||
## Next steps
|
||||
|
||||
* Read our [code conventions](coding-conventions.md)
|
||||
* Learn how to [Create a pull request](create-pull-request.md)
|
||||
- Read our [code conventions](coding-conventions.md)
|
||||
- Learn how to [Create a pull request](create-pull-request.md)
|
||||
|
||||
186
go.mod
186
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/oam-dev/kubevela
|
||||
|
||||
go 1.16
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
cuelang.org/go v0.2.2
|
||||
@@ -90,7 +90,191 @@ require (
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.81.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.16 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.14 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/agext/levenshtein v1.2.2 // indirect
|
||||
github.com/alessio/shellescape v1.2.2 // indirect
|
||||
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect
|
||||
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
|
||||
github.com/alibabacloud-go/openapi-util v0.0.7 // indirect
|
||||
github.com/alibabacloud-go/tea-utils v1.3.9 // indirect
|
||||
github.com/aliyun/credentials-go v1.1.2 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
|
||||
github.com/aws/aws-sdk-go v1.36.30 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||
github.com/cockroachdb/apd/v2 v2.0.1 // indirect
|
||||
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59 // indirect
|
||||
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/creack/pty v1.1.11 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.2 // indirect
|
||||
github.com/deislabs/oras v0.11.1 // indirect
|
||||
github.com/docker/cli v20.10.5+incompatible // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.3 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
|
||||
github.com/emicklei/proto v1.6.15 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.1.0 // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect
|
||||
github.com/fatih/camelcase v1.0.0 // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-logr/zapr v0.4.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/gobuffalo/flect v0.2.3 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/klauspost/compress v1.11.0 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/pty v1.1.8 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lib/pq v1.10.0 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.3 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||
github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 // indirect
|
||||
github.com/russross/blackfriday v1.5.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/src-d/gcfg v1.4.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tjfoc/gmsm v1.3.2 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.0.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
github.com/zclconf/go-cty v1.8.0 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
google.golang.org/grpc v1.38.0 // indirect
|
||||
google.golang.org/protobuf v1.26.0 // indirect
|
||||
gopkg.in/gorp.v1 v1.7.2 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
istio.io/api v0.0.0-20210128181506-0c4b8e54850f // indirect
|
||||
istio.io/gogo-genproto v0.0.0-20190930162913-45029607206a // indirect
|
||||
k8s.io/apiserver v0.22.1 // indirect
|
||||
k8s.io/component-base v0.22.1 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy v0.0.24 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.24 // indirect
|
||||
sigs.k8s.io/apiserver-runtime v1.0.3-0.20210913073608-0663f60bfee2 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.8.5 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.10.15 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/docker/cli => github.com/docker/cli v20.10.9+incompatible
|
||||
github.com/docker/docker => github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible
|
||||
github.com/wercker/stern => github.com/oam-dev/stern v1.13.2
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client => sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.24
|
||||
|
||||
4
go.sum
4
go.sum
@@ -406,8 +406,8 @@ github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMa
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/cli v20.10.5+incompatible h1:bjflayQbWg+xOkF2WPEAOi4Y7zWhR7ptoPhV/VqLVDE=
|
||||
github.com/docker/cli v20.10.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v20.10.9+incompatible h1:OJ7YkwQA+k2Oi51lmCojpjiygKpi76P7bg91b2eJxYU=
|
||||
github.com/docker/cli v20.10.9+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
|
||||
@@ -37,7 +37,7 @@ goimports:
|
||||
ifeq (, $(shell which goimports))
|
||||
@{ \
|
||||
set -e ;\
|
||||
GO111MODULE=off go get -u golang.org/x/tools/cmd/goimports ;\
|
||||
go install golang.org/x/tools/cmd/goimports@latest ;\
|
||||
}
|
||||
GOIMPORTS=$(GOBIN)/goimports
|
||||
else
|
||||
@@ -49,7 +49,7 @@ installcue:
|
||||
ifeq (, $(shell which cue))
|
||||
@{ \
|
||||
set -e ;\
|
||||
GO111MODULE=off go get -u cuelang.org/go/cmd/cue ;\
|
||||
go install cuelang.org/go/cmd/cue@latest ;\
|
||||
}
|
||||
CUE=$(GOBIN)/cue
|
||||
else
|
||||
|
||||
@@ -504,6 +504,7 @@ func generateComponentFromTerraformModule(wl *Workload, appName, ns string) (*ty
|
||||
|
||||
func baseGenerateComponent(pCtx process.Context, wl *Workload, appName, ns string) (*types.ComponentManifest, error) {
|
||||
var err error
|
||||
pCtx.PushData(model.ContextComponentType, wl.Type)
|
||||
for _, tr := range wl.Traits {
|
||||
if err := tr.EvalContext(pCtx); err != nil {
|
||||
return nil, errors.Wrapf(err, "evaluate template trait=%s app=%s", tr.Name, wl.Name)
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/test"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
terraformtypes "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime"
|
||||
@@ -1322,3 +1323,47 @@ spec:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBaseGenerateComponent(t *testing.T) {
|
||||
var appName = "test-app"
|
||||
var ns = "test-ns"
|
||||
var traitName = "mytrait"
|
||||
var wlName = "my-wl-1"
|
||||
pContext := NewBasicContext(appName, wlName, "rev-1", ns, nil)
|
||||
base := `
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: {
|
||||
template: {
|
||||
spec: containers: [{
|
||||
image: "nginx"
|
||||
}]
|
||||
}
|
||||
}
|
||||
`
|
||||
var r cue.Runtime
|
||||
inst, err := r.Compile("-", base)
|
||||
assert.NilError(t, err)
|
||||
bs, _ := model.NewBase(inst.Value())
|
||||
err = pContext.SetBase(bs)
|
||||
assert.NilError(t, err)
|
||||
tr := &Trait{
|
||||
Name: traitName,
|
||||
engine: definition.NewTraitAbstractEngine(traitName, nil),
|
||||
Template: `outputs:mytrait:{
|
||||
if context.componentType == "stateless" {
|
||||
kind: "Deployment"
|
||||
}
|
||||
if context.componentType == "stateful" {
|
||||
kind: "StatefulSet"
|
||||
}
|
||||
name: context.name
|
||||
envSourceContainerName: context.name
|
||||
}`,
|
||||
}
|
||||
wl := &Workload{Type: "stateful", Traits: []*Trait{tr}}
|
||||
cm, err := baseGenerateComponent(pContext, wl, appName, ns)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, cm.Traits[0].Object["kind"], "StatefulSet")
|
||||
assert.Equal(t, cm.Traits[0].Object["name"], wlName)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcetracker"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -87,15 +86,13 @@ func DefaultNewControllerClient(cache cache.Cache, config *rest.Config, options
|
||||
AddFunc: func(obj interface{}) {
|
||||
lock.Lock()
|
||||
rtCount++
|
||||
metrics.ResourceTrackerNumberGauge.WithLabelValues(
|
||||
metrics.ExtractMetricValuesFromObjectLabel(obj, oam.LabelAppName, oam.LabelAppNamespace)...).Set(float64(rtCount))
|
||||
metrics.ResourceTrackerNumberGauge.WithLabelValues("application").Set(float64(rtCount))
|
||||
lock.Unlock()
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
lock.Lock()
|
||||
rtCount--
|
||||
metrics.ResourceTrackerNumberGauge.WithLabelValues(
|
||||
metrics.ExtractMetricValuesFromObjectLabel(obj, oam.LabelAppName, oam.LabelAppNamespace)...).Set(float64(rtCount))
|
||||
metrics.ResourceTrackerNumberGauge.WithLabelValues("application").Set(float64(rtCount))
|
||||
lock.Unlock()
|
||||
},
|
||||
})
|
||||
|
||||
@@ -2275,7 +2275,51 @@ var _ = Describe("Test Application Controller", func() {
|
||||
Namespace: app.Namespace,
|
||||
}, checkWeb)).Should(BeNil())
|
||||
Expect(*(checkWeb.Spec.Replicas)).Should(BeEquivalentTo(int32(0)))
|
||||
})
|
||||
|
||||
It("app apply resource in parallel", func() {
|
||||
wfDef := &v1beta1.WorkflowStepDefinition{}
|
||||
wfDefJson, _ := yaml.YAMLToJSON([]byte(applyInParallelWorkflowDefinitionYaml))
|
||||
Expect(json.Unmarshal(wfDefJson, wfDef)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, wfDef.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "vela-test-apply-in-parallel",
|
||||
},
|
||||
}
|
||||
app := appwithNoTrait.DeepCopy()
|
||||
app.Name = "vela-test-app"
|
||||
app.SetNamespace(ns.Name)
|
||||
app.Spec.Workflow = &v1beta1.Workflow{
|
||||
Steps: []v1beta1.WorkflowStep{{
|
||||
Name: "apply-in-parallel",
|
||||
Type: "apply-test",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"parallelism": 20}`)},
|
||||
}},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, ns)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
|
||||
appKey := client.ObjectKey{
|
||||
Name: app.Name,
|
||||
Namespace: app.Namespace,
|
||||
}
|
||||
_, err := testutil.ReconcileOnceAfterFinalizer(reconciler, reconcile.Request{NamespacedName: appKey})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
deployList := new(v1.DeploymentList)
|
||||
Expect(k8sClient.List(ctx, deployList, client.InNamespace(app.Namespace))).Should(BeNil())
|
||||
Expect(len(deployList.Items)).Should(Equal(20))
|
||||
|
||||
checkApp := new(v1beta1.Application)
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(Succeed())
|
||||
rt := new(v1beta1.ResourceTracker)
|
||||
expectRTName := fmt.Sprintf("%s-%s", checkApp.Status.LatestRevision.Name, checkApp.GetNamespace())
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: expectRTName}, rt)
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
Expect(len(rt.Spec.ManagedResources)).Should(Equal(20))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3184,6 +3228,46 @@ spec:
|
||||
}
|
||||
}
|
||||
parameter: objects: [...{}]
|
||||
`
|
||||
applyInParallelWorkflowDefinitionYaml = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: WorkflowStepDefinition
|
||||
metadata:
|
||||
name: apply-test
|
||||
namespace: vela-system
|
||||
spec:
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
import (
|
||||
"vela/op"
|
||||
"list"
|
||||
)
|
||||
|
||||
components: op.#LoadInOrder & {}
|
||||
targetComponent: components.value[0]
|
||||
resources: op.#RenderComponent & {
|
||||
value: targetComponent
|
||||
}
|
||||
workload: resources.output
|
||||
arr: list.Range(0, parameter.parallelism, 1)
|
||||
patchWorkloads: op.#Steps & {
|
||||
for idx in arr {
|
||||
"\(idx)": op.#PatchK8sObject & {
|
||||
value: workload
|
||||
patch: {
|
||||
// +patchStrategy=retainKeys
|
||||
metadata: name: "\(targetComponent.name)-\(idx)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
workloads: [ for patchResult in patchWorkloads {patchResult.result}]
|
||||
apply: op.#ApplyInParallel & {
|
||||
value: workloads
|
||||
}
|
||||
parameter: parallelism: int
|
||||
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
@@ -374,11 +374,17 @@ func (h *AppHandler) currentAppRevIsNew(ctx context.Context) (bool, bool, error)
|
||||
return true, true, nil
|
||||
}
|
||||
|
||||
isLatestRev := deepEqualAppInRevision(h.latestAppRev, h.currentAppRev)
|
||||
if metav1.HasAnnotation(h.app.ObjectMeta, oam.AnnotationAutoUpdate) {
|
||||
isLatestRev = h.app.Status.LatestRevision.RevisionHash == h.currentRevHash && DeepEqualRevision(h.latestAppRev, h.currentAppRev)
|
||||
}
|
||||
|
||||
// diff the latest revision first
|
||||
if h.app.Status.LatestRevision.RevisionHash == h.currentRevHash && DeepEqualRevision(h.latestAppRev, h.currentAppRev) {
|
||||
if isLatestRev {
|
||||
appSpec := h.currentAppRev.Spec.Application.Spec
|
||||
traitDef := h.currentAppRev.Spec.TraitDefinitions
|
||||
h.currentAppRev = h.latestAppRev.DeepCopy()
|
||||
h.currentRevHash = h.app.Status.LatestRevision.RevisionHash
|
||||
h.currentAppRev.Spec.Application.Spec = appSpec
|
||||
h.currentAppRev.Spec.TraitDefinitions = traitDef
|
||||
return false, false, nil
|
||||
@@ -444,6 +450,10 @@ func DeepEqualRevision(old, new *v1beta1.ApplicationRevision) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return deepEqualAppInRevision(old, new)
|
||||
}
|
||||
|
||||
func deepEqualAppInRevision(old, new *v1beta1.ApplicationRevision) bool {
|
||||
return apiequality.Semantic.DeepEqual(filterSkipAffectAppRevTrait(old.Spec.Application.Spec, old.Spec.TraitDefinitions),
|
||||
filterSkipAffectAppRevTrait(new.Spec.Application.Spec, new.Spec.TraitDefinitions))
|
||||
}
|
||||
|
||||
@@ -245,6 +245,28 @@ var _ = Describe("test generate revision ", func() {
|
||||
verifyDeepEqualRevision()
|
||||
})
|
||||
|
||||
It("Test application revision compare", func() {
|
||||
By("Apply the application")
|
||||
appParser := appfile.NewApplicationParser(reconciler.Client, reconciler.dm, reconciler.pd)
|
||||
ctx = util.SetNamespaceInCtx(ctx, app.Namespace)
|
||||
generatedAppfile, err := appParser.GenerateAppFile(ctx, &app)
|
||||
Expect(err).Should(Succeed())
|
||||
comps, err = generatedAppfile.GenerateComponentManifests()
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(handler.PrepareCurrentAppRevision(ctx, generatedAppfile)).Should(Succeed())
|
||||
Expect(handler.FinalizeAndApplyAppRevision(ctx)).Should(Succeed())
|
||||
prevHash := generatedAppfile.AppRevisionHash
|
||||
handler.app.Status.LatestRevision = &common.Revision{Name: generatedAppfile.AppRevisionName, Revision: 1, RevisionHash: generatedAppfile.AppRevisionHash}
|
||||
generatedAppfile.Workloads[0].FullTemplate.ComponentDefinition = nil
|
||||
Expect(handler.PrepareCurrentAppRevision(ctx, generatedAppfile)).Should(Succeed())
|
||||
nonChangeHash := generatedAppfile.AppRevisionHash
|
||||
handler.app.Annotations = map[string]string{oam.AnnotationAutoUpdate: "true"}
|
||||
Expect(handler.PrepareCurrentAppRevision(ctx, generatedAppfile)).Should(Succeed())
|
||||
changedHash := generatedAppfile.AppRevisionHash
|
||||
Expect(nonChangeHash).Should(Equal(prevHash))
|
||||
Expect(changedHash).ShouldNot(Equal(prevHash))
|
||||
})
|
||||
|
||||
It("Test apply success for none rollout case", func() {
|
||||
By("Apply the application")
|
||||
appParser := appfile.NewApplicationParser(reconciler.Client, reconciler.dm, reconciler.pd)
|
||||
|
||||
@@ -61,11 +61,13 @@ const (
|
||||
TerraformVariableMap string = "map"
|
||||
TerraformVariableObject string = "object"
|
||||
TerraformVariableNull string = ""
|
||||
TerraformVariableAny string = "any"
|
||||
|
||||
TerraformListTypePrefix string = "list("
|
||||
TerraformTupleTypePrefix string = "tuple("
|
||||
TerraformMapTypePrefix string = "map("
|
||||
TerraformObjectTypePrefix string = "object("
|
||||
TerraformSetTypePrefix string = "set("
|
||||
|
||||
typeTraitDefinition = "trait"
|
||||
typeComponentDefinition = "component"
|
||||
@@ -157,17 +159,38 @@ func GetOpenAPISchemaFromTerraformComponentDefinition(configuration string) ([]b
|
||||
schema = openapi3.NewArraySchema()
|
||||
case TerraformVariableMap, TerraformVariableObject:
|
||||
schema = openapi3.NewObjectSchema()
|
||||
case TerraformVariableAny:
|
||||
switch v.Default.(type) {
|
||||
case []interface{}:
|
||||
schema = openapi3.NewArraySchema()
|
||||
case map[string]interface{}:
|
||||
schema = openapi3.NewObjectSchema()
|
||||
}
|
||||
case TerraformVariableNull:
|
||||
return nil, fmt.Errorf("null type variable is NOT supported, please specify a type for the variable: %s", v.Name)
|
||||
switch v.Default.(type) {
|
||||
case nil, string:
|
||||
schema = openapi3.NewStringSchema()
|
||||
case []interface{}:
|
||||
schema = openapi3.NewArraySchema()
|
||||
case map[string]interface{}:
|
||||
schema = openapi3.NewObjectSchema()
|
||||
case int, float64:
|
||||
schema = openapi3.NewFloat64Schema()
|
||||
default:
|
||||
return nil, fmt.Errorf("null type variable is NOT supported, please specify a type for the variable: %s", v.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// To identify unusual list type
|
||||
if schema == nil {
|
||||
switch {
|
||||
case strings.HasPrefix(v.Type, TerraformListTypePrefix) || strings.HasPrefix(v.Type, TerraformTupleTypePrefix):
|
||||
case strings.HasPrefix(v.Type, TerraformListTypePrefix) || strings.HasPrefix(v.Type, TerraformTupleTypePrefix) ||
|
||||
strings.HasPrefix(v.Type, TerraformSetTypePrefix):
|
||||
schema = openapi3.NewArraySchema()
|
||||
case strings.HasPrefix(v.Type, TerraformMapTypePrefix) || strings.HasPrefix(v.Type, TerraformObjectTypePrefix):
|
||||
schema = openapi3.NewObjectSchema()
|
||||
default:
|
||||
return nil, fmt.Errorf("the type `%s` of variable %s is NOT supported", v.Type, v.Name)
|
||||
}
|
||||
}
|
||||
schema.Title = k
|
||||
|
||||
@@ -20,6 +20,7 @@ package utils
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -326,8 +327,38 @@ variable "name" {
|
||||
default = "abc"
|
||||
}`,
|
||||
want: want{
|
||||
subStr: "",
|
||||
err: errors.New("null type variable is NOT supported, please specify a type for the variable: name"),
|
||||
subStr: "abc",
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"null type variable, while default value is a slice": {
|
||||
configuration: `
|
||||
variable "name" {
|
||||
default = [123]
|
||||
}`,
|
||||
want: want{
|
||||
subStr: "123",
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"null type variable, while default value is a map": {
|
||||
configuration: `
|
||||
variable "name" {
|
||||
default = {a = 1}
|
||||
}`,
|
||||
want: want{
|
||||
subStr: "a",
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"null type variable, while default value is number": {
|
||||
configuration: `
|
||||
variable "name" {
|
||||
default = 123
|
||||
}`,
|
||||
want: want{
|
||||
subStr: "123",
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"complicated list variable": {
|
||||
@@ -354,6 +385,38 @@ variable "bbb" {
|
||||
config = string
|
||||
})
|
||||
default = []
|
||||
}`,
|
||||
want: want{
|
||||
subStr: "bbb",
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"not supported complicated variable": {
|
||||
configuration: `
|
||||
variable "bbb" {
|
||||
type = xxxxx(string)
|
||||
}`,
|
||||
want: want{
|
||||
subStr: "",
|
||||
err: fmt.Errorf("the type `%s` of variable %s is NOT supported", "xxxxx(string)", "bbb"),
|
||||
},
|
||||
},
|
||||
"any type, slice default": {
|
||||
configuration: `
|
||||
variable "bbb" {
|
||||
type = any
|
||||
default = []
|
||||
}`,
|
||||
want: want{
|
||||
subStr: "bbb",
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
"any type, map default": {
|
||||
configuration: `
|
||||
variable "bbb" {
|
||||
type = any
|
||||
default = {}
|
||||
}`,
|
||||
want: want{
|
||||
subStr: "bbb",
|
||||
@@ -423,6 +486,34 @@ variable "aaa" {
|
||||
config = string
|
||||
}))
|
||||
default = []
|
||||
}`,
|
||||
},
|
||||
},
|
||||
"configuration is remote with path": {
|
||||
args: args{
|
||||
name: "aws-subnet",
|
||||
url: "https://github.com/kubevela-contrib/terraform-modules.git",
|
||||
path: "aws/subnet",
|
||||
data: []byte(`
|
||||
variable "aaa" {
|
||||
type = list(object({
|
||||
type = string
|
||||
sourceArn = string
|
||||
config = string
|
||||
}))
|
||||
default = []
|
||||
}`),
|
||||
variableFile: "variables.tf",
|
||||
},
|
||||
want: want{
|
||||
config: `
|
||||
variable "aaa" {
|
||||
type = list(object({
|
||||
type = string
|
||||
sourceArn = string
|
||||
config = string
|
||||
}))
|
||||
default = []
|
||||
}`,
|
||||
},
|
||||
},
|
||||
@@ -454,7 +545,12 @@ variable "aaa" {
|
||||
}
|
||||
|
||||
patch := ApplyFunc(git.PlainCloneContext, func(ctx context.Context, path string, isBare bool, o *git.CloneOptions) (*git.Repository, error) {
|
||||
tmpPath := filepath.Join("./tmp/terraform", tc.args.name)
|
||||
var tmpPath string
|
||||
if tc.args.path != "" {
|
||||
tmpPath = filepath.Join("./tmp/terraform", tc.args.name, tc.args.path)
|
||||
} else {
|
||||
tmpPath = filepath.Join("./tmp/terraform", tc.args.name)
|
||||
}
|
||||
err := os.MkdirAll(tmpPath, os.ModePerm)
|
||||
assert.NilError(t, err)
|
||||
err = ioutil.WriteFile(filepath.Clean(filepath.Join(tmpPath, tc.args.variableFile)), tc.args.data, 0644)
|
||||
|
||||
@@ -41,6 +41,8 @@ const (
|
||||
ContextCompRevisionName = "revision"
|
||||
// ContextComponents is the components of app
|
||||
ContextComponents = "components"
|
||||
// ContextComponentType is the component type of current trait binding with
|
||||
ContextComponentType = "componentType"
|
||||
// ComponentRevisionPlaceHolder is the component revision name placeHolder, this field will be replace with real value
|
||||
// after component be created
|
||||
ComponentRevisionPlaceHolder = "KUBEVELA_COMPONENT_REVISION_PLACEHOLDER"
|
||||
|
||||
@@ -107,18 +107,17 @@ func (pd *PackageDiscover) ImportBuiltinPackagesFor(bi *build.Instance) {
|
||||
|
||||
// ImportPackagesAndBuildInstance Combine import built-in packages and build cue template together to avoid data race
|
||||
func (pd *PackageDiscover) ImportPackagesAndBuildInstance(bi *build.Instance) (inst *cue.Instance, err error) {
|
||||
var r cue.Runtime
|
||||
if pd == nil {
|
||||
return r.Build(bi)
|
||||
}
|
||||
pd.ImportBuiltinPackagesFor(bi)
|
||||
if err := stdlib.AddImportsFor(bi, ""); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var r cue.Runtime
|
||||
pd.mutex.Lock()
|
||||
defer pd.mutex.Unlock()
|
||||
cueInst, err := r.Build(bi)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cueInst, err
|
||||
return r.Build(bi)
|
||||
}
|
||||
|
||||
// ListPackageKinds list packages and their kinds
|
||||
|
||||
@@ -18,7 +18,6 @@ package metrics
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -116,17 +115,5 @@ var (
|
||||
ResourceTrackerNumberGauge = prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "resourcetracker_number",
|
||||
Help: "resourceTracker number.",
|
||||
}, []string{"application", "namespace"})
|
||||
}, []string{"controller"})
|
||||
)
|
||||
|
||||
// ExtractMetricValuesFromObjectLabel extract metric values from k8s object's labels
|
||||
func ExtractMetricValuesFromObjectLabel(obj interface{}, labelKeys ...string) (values []string) {
|
||||
if resource, ok := obj.(client.Object); ok {
|
||||
for _, labelKey := range labelKeys {
|
||||
values = append(values, resource.GetLabels()[labelKey])
|
||||
}
|
||||
} else {
|
||||
values = make([]string, len(labelKeys))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -165,6 +165,9 @@ const (
|
||||
// AnnotationPublishVersion is annotation that record the application workflow version.
|
||||
AnnotationPublishVersion = "app.oam.dev/publishVersion"
|
||||
|
||||
// AnnotationAutoUpdate is annotation that let application auto update when it finds definition changes
|
||||
AnnotationAutoUpdate = "app.oam.dev/autoUpdate"
|
||||
|
||||
// AnnotationWorkflowName specifies the workflow name for execution.
|
||||
AnnotationWorkflowName = "app.oam.dev/workflowName"
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ func (h *resourceKeeper) DispatchComponentRevision(ctx context.Context, cr *v1.C
|
||||
obj.SetName(cr.Name)
|
||||
obj.SetNamespace(cr.Namespace)
|
||||
obj.SetLabels(cr.Labels)
|
||||
if err = resourcetracker.RecordManifestInResourceTracker(multicluster.ContextInLocalCluster(ctx), h.Client, rt, obj, true); err != nil {
|
||||
if err = resourcetracker.RecordManifestsInResourceTracker(multicluster.ContextInLocalCluster(ctx), h.Client, rt, []*unstructured.Unstructured{obj}, true); err != nil {
|
||||
return errors.Wrapf(err, "failed to record componentrevision %s/%s/%s", oam.GetCluster(cr), cr.Namespace, cr.Name)
|
||||
}
|
||||
if err = h.Client.Create(multicluster.ContextWithClusterName(ctx, oam.GetCluster(cr)), cr); err != nil {
|
||||
|
||||
@@ -18,17 +18,21 @@ package resourcekeeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
kerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcetracker"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
// MaxDispatchConcurrent is the max dispatch concurrent number
|
||||
var MaxDispatchConcurrent = 10
|
||||
|
||||
// DispatchOption option for dispatch
|
||||
type DispatchOption interface {
|
||||
ApplyToDispatchConfig(*dispatchConfig)
|
||||
@@ -52,6 +56,21 @@ func (h *resourceKeeper) Dispatch(ctx context.Context, manifests []*unstructured
|
||||
if h.applyOncePolicy != nil && h.applyOncePolicy.Enable {
|
||||
options = append(options, MetaOnlyOption{})
|
||||
}
|
||||
// 1. record manifests in resourcetracker
|
||||
if err = h.record(ctx, manifests, options...); err != nil {
|
||||
return err
|
||||
}
|
||||
// 2. apply manifests
|
||||
if err = h.dispatch(ctx, manifests); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *resourceKeeper) record(ctx context.Context, manifests []*unstructured.Unstructured, options ...DispatchOption) error {
|
||||
var rootManifests []*unstructured.Unstructured
|
||||
var versionManifests []*unstructured.Unstructured
|
||||
|
||||
for _, manifest := range manifests {
|
||||
if manifest != nil {
|
||||
_options := options
|
||||
@@ -61,34 +80,61 @@ func (h *resourceKeeper) Dispatch(ctx context.Context, manifests []*unstructured
|
||||
}
|
||||
}
|
||||
cfg := newDispatchConfig(_options...)
|
||||
if err = h.dispatch(ctx, manifest, cfg); err != nil {
|
||||
return err
|
||||
if !cfg.skipRT {
|
||||
if cfg.useRoot {
|
||||
rootManifests = append(rootManifests, manifest)
|
||||
} else {
|
||||
versionManifests = append(versionManifests, manifest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg := newDispatchConfig(options...)
|
||||
if len(rootManifests) != 0 {
|
||||
rt, err := h.getRootRT(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get resourcetracker")
|
||||
}
|
||||
if err = resourcetracker.RecordManifestsInResourceTracker(multicluster.ContextInLocalCluster(ctx), h.Client, rt, rootManifests, cfg.metaOnly); err != nil {
|
||||
return errors.Wrapf(err, "failed to record resources in resourcetracker %s", rt.Name)
|
||||
}
|
||||
}
|
||||
|
||||
rt, err := h.getCurrentRT(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get resourcetracker")
|
||||
}
|
||||
if err = resourcetracker.RecordManifestsInResourceTracker(multicluster.ContextInLocalCluster(ctx), h.Client, rt, versionManifests, cfg.metaOnly); err != nil {
|
||||
return errors.Wrapf(err, "failed to record resources in resourcetracker %s", rt.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *resourceKeeper) dispatch(ctx context.Context, manifest *unstructured.Unstructured, cfg *dispatchConfig) (err error) {
|
||||
// 1. record manifests in resourcetracker
|
||||
if !cfg.skipRT {
|
||||
var rt *v1beta1.ResourceTracker
|
||||
if cfg.useRoot {
|
||||
rt, err = h.getRootRT(ctx)
|
||||
} else {
|
||||
rt, err = h.getCurrentRT(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get resourcetracker")
|
||||
}
|
||||
if err = resourcetracker.RecordManifestInResourceTracker(multicluster.ContextInLocalCluster(ctx), h.Client, rt, manifest, cfg.metaOnly); err != nil {
|
||||
return errors.Wrapf(err, "failed to record resources in resourcetracker %s", rt.Name)
|
||||
}
|
||||
}
|
||||
// 2. apply manifests
|
||||
func (h *resourceKeeper) dispatch(ctx context.Context, manifests []*unstructured.Unstructured) error {
|
||||
var errs []error
|
||||
var l sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
|
||||
ch := make(chan struct{}, MaxDispatchConcurrent)
|
||||
applyOpts := []apply.ApplyOption{apply.MustBeControlledByApp(h.app), apply.NotUpdateRenderHashEqual()}
|
||||
if err := h.applicator.Apply(multicluster.ContextWithClusterName(ctx, oam.GetCluster(manifest)), manifest, applyOpts...); err != nil {
|
||||
return errors.Wrapf(err, "cannot apply manifest, name: %s apiVersion: %s kind: %s", manifest.GetName(), manifest.GetAPIVersion(), manifest.GetKind())
|
||||
|
||||
for i := 0; i < len(manifests); i++ {
|
||||
ch <- struct{}{}
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
defer wg.Done()
|
||||
manifest := manifests[index]
|
||||
applyCtx := multicluster.ContextWithClusterName(ctx, oam.GetCluster(manifest))
|
||||
err := h.applicator.Apply(applyCtx, manifest, applyOpts...)
|
||||
if err != nil {
|
||||
l.Lock()
|
||||
errs = append(errs, err)
|
||||
l.Unlock()
|
||||
}
|
||||
<-ch
|
||||
}(i)
|
||||
}
|
||||
return nil
|
||||
wg.Wait()
|
||||
return kerrors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
@@ -101,9 +101,9 @@ func TestResourceKeeperGarbageCollect(t *testing.T) {
|
||||
obj.SetName(cr.GetName())
|
||||
obj.SetNamespace(cr.GetNamespace())
|
||||
obj.SetLabels(cr.GetLabels())
|
||||
r.NoError(resourcetracker.RecordManifestInResourceTracker(ctx, cli, crRT, obj, true))
|
||||
r.NoError(resourcetracker.RecordManifestsInResourceTracker(ctx, cli, crRT, []*unstructured.Unstructured{obj}, true))
|
||||
}
|
||||
r.NoError(resourcetracker.RecordManifestInResourceTracker(ctx, cli, _rt, cmMaps[i], true))
|
||||
r.NoError(resourcetracker.RecordManifestsInResourceTracker(ctx, cli, _rt, []*unstructured.Unstructured{cmMaps[i]}, true))
|
||||
}
|
||||
|
||||
checkCount := func(cmCount, rtCount int, crCount int) {
|
||||
|
||||
@@ -142,12 +142,15 @@ func ListApplicationResourceTrackers(ctx context.Context, cli client.Client, app
|
||||
return rootRT, currentRT, historyRTs, crRT, nil
|
||||
}
|
||||
|
||||
// RecordManifestInResourceTracker records resources in ResourceTracker
|
||||
func RecordManifestInResourceTracker(ctx context.Context, cli client.Client, rt *v1beta1.ResourceTracker, manifest *unstructured.Unstructured, metaOnly bool) error {
|
||||
if updated := rt.AddManagedResource(manifest, metaOnly); !updated {
|
||||
return nil
|
||||
// RecordManifestsInResourceTracker records resources in ResourceTracker
|
||||
func RecordManifestsInResourceTracker(ctx context.Context, cli client.Client, rt *v1beta1.ResourceTracker, manifests []*unstructured.Unstructured, metaOnly bool) error {
|
||||
if len(manifests) != 0 {
|
||||
for _, manifest := range manifests {
|
||||
rt.AddManagedResource(manifest, metaOnly)
|
||||
}
|
||||
return cli.Update(ctx, rt)
|
||||
}
|
||||
return cli.Update(ctx, rt)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeletedManifestInResourceTracker marks resources as deleted in resourcetracker, if remove is true, resources will be removed from resourcetracker
|
||||
|
||||
@@ -97,7 +97,7 @@ func TestRecordAndDeleteManifestsInResourceTracker(t *testing.T) {
|
||||
obj := &unstructured.Unstructured{}
|
||||
obj.SetName(fmt.Sprintf("workload-%d", i))
|
||||
objs = append(objs, obj)
|
||||
r.NoError(RecordManifestInResourceTracker(context.Background(), cli, rt, obj, rand.Int()%2 == 0))
|
||||
r.NoError(RecordManifestsInResourceTracker(context.Background(), cli, rt, []*unstructured.Unstructured{obj}, rand.Int()%2 == 0))
|
||||
}
|
||||
rand.Shuffle(len(objs), func(i, j int) { objs[i], objs[j] = objs[j], objs[i] })
|
||||
for i := 0; i < n; i++ {
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
|
||||
#Apply: kube.#Apply
|
||||
|
||||
#ApplyInParallel: kube.#ApplyInParallel
|
||||
|
||||
#Read: kube.#Read
|
||||
|
||||
#List: kube.#List
|
||||
@@ -156,7 +158,7 @@ import (
|
||||
|
||||
#HTTPDelete: http.#Do & {method: "DELETE"}
|
||||
|
||||
#ConvertString: convert.#String
|
||||
#ConvertString: util.#String
|
||||
|
||||
#DateToTimestamp: time.#DateToTimestamp
|
||||
|
||||
@@ -168,6 +170,8 @@ import (
|
||||
|
||||
#LoadInOrder: oam.#LoadComponetsInOrder
|
||||
|
||||
#PatchK8sObject: util.#PatchK8sObject
|
||||
|
||||
#Steps: {
|
||||
#do: "steps"
|
||||
...
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#String: {
|
||||
#do: "string"
|
||||
#provider: "convert"
|
||||
|
||||
bt: bytes
|
||||
str?: string
|
||||
...
|
||||
}
|
||||
@@ -6,6 +6,14 @@
|
||||
...
|
||||
}
|
||||
|
||||
#ApplyInParallel: {
|
||||
#do: "apply-in-parallel"
|
||||
#provider: "kube"
|
||||
cluster: *"" | string
|
||||
value: [...{...}]
|
||||
...
|
||||
}
|
||||
|
||||
#Read: {
|
||||
#do: "read"
|
||||
#provider: "kube"
|
||||
|
||||
17
pkg/stdlib/pkgs/util.cue
Normal file
17
pkg/stdlib/pkgs/util.cue
Normal file
@@ -0,0 +1,17 @@
|
||||
#PatchK8sObject: {
|
||||
#do: "patch-k8s-object"
|
||||
#provider: "util"
|
||||
value: {...}
|
||||
patch: {...}
|
||||
result: {...}
|
||||
...
|
||||
}
|
||||
|
||||
#String: {
|
||||
#do: "string"
|
||||
#provider: "util"
|
||||
|
||||
bt: bytes
|
||||
str?: string
|
||||
...
|
||||
}
|
||||
252
pkg/utils/helm/helm_helper.go
Normal file
252
pkg/utils/helm/helm_helper.go
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
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 helm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/kube"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
"helm.sh/helm/v3/pkg/storage"
|
||||
"helm.sh/helm/v3/pkg/storage/driver"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
kyaml "k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/client-go/rest"
|
||||
k8scmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
// Helper provides helper functions for common Helm operations
|
||||
type Helper struct {
|
||||
}
|
||||
|
||||
// NewHelper creates a Helper
|
||||
func NewHelper() *Helper {
|
||||
return &Helper{}
|
||||
}
|
||||
|
||||
// LoadCharts load helm chart from local or remote
|
||||
func (h *Helper) LoadCharts(chartRepoURL string) (*chart.Chart, error) {
|
||||
var err error
|
||||
var chart *chart.Chart
|
||||
if utils.IsValidURL(chartRepoURL) {
|
||||
chartBytes, err := common.HTTPGet(context.Background(), chartRepoURL)
|
||||
if err != nil {
|
||||
return nil, errors.New("error retrieving Helm Chart at " + chartRepoURL + ": " + err.Error())
|
||||
}
|
||||
ch, err := loader.LoadArchive(bytes.NewReader(chartBytes))
|
||||
if err != nil {
|
||||
return nil, errors.New("error retrieving Helm Chart at " + chartRepoURL + ": " + err.Error())
|
||||
}
|
||||
return ch, err
|
||||
}
|
||||
chart, err = loader.Load(chartRepoURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return chart, nil
|
||||
}
|
||||
|
||||
// UpgradeChartOptions options for upgrade chart
|
||||
type UpgradeChartOptions struct {
|
||||
Config *rest.Config
|
||||
Detail bool
|
||||
Logging cmdutil.IOStreams
|
||||
Wait bool
|
||||
ReuseValues bool
|
||||
}
|
||||
|
||||
// UpgradeChart install or upgrade helm chart
|
||||
func (h *Helper) UpgradeChart(ch *chart.Chart, releaseName, namespace string, values map[string]interface{}, config UpgradeChartOptions) (*release.Release, error) {
|
||||
if ch == nil || len(ch.Templates) == 0 {
|
||||
return nil, fmt.Errorf("empty chart provided for %s", releaseName)
|
||||
}
|
||||
config.Logging.Infof("Start upgrading Helm Chart %s in namespace %s\n", releaseName, namespace)
|
||||
|
||||
cfg, err := newActionConfig(config.Config, namespace, config.Detail, config.Logging)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
histClient := action.NewHistory(cfg)
|
||||
var newRelease *release.Release
|
||||
timeoutInMinutes := 18
|
||||
releases, err := histClient.Run(releaseName)
|
||||
if err != nil {
|
||||
if errors.Is(err, driver.ErrReleaseNotFound) {
|
||||
// fresh install
|
||||
install := action.NewInstall(cfg)
|
||||
install.Namespace = namespace
|
||||
install.ReleaseName = releaseName
|
||||
install.Wait = config.Wait
|
||||
install.Timeout = time.Duration(timeoutInMinutes) * time.Minute
|
||||
newRelease, err = install.Run(ch, values)
|
||||
} else {
|
||||
return nil, fmt.Errorf("could not retrieve history of releases associated to %s: %w", releaseName, err)
|
||||
}
|
||||
} else {
|
||||
config.Logging.Infof("Found existing installation, overwriting...")
|
||||
|
||||
// check if the previous installation is still pending (e.g., waiting to complete)
|
||||
for _, r := range releases {
|
||||
if r.Info.Status == release.StatusPendingInstall || r.Info.Status == release.StatusPendingUpgrade ||
|
||||
r.Info.Status == release.StatusPendingRollback {
|
||||
return nil, fmt.Errorf("previous installation (e.g., using vela install or helm upgrade) is still in progress. Please try again in %d minutes", timeoutInMinutes)
|
||||
}
|
||||
}
|
||||
|
||||
// overwrite existing installation
|
||||
install := action.NewUpgrade(cfg)
|
||||
install.Namespace = namespace
|
||||
install.Wait = config.Wait
|
||||
install.Timeout = time.Duration(timeoutInMinutes) * time.Minute
|
||||
install.ReuseValues = config.ReuseValues
|
||||
newRelease, err = install.Run(releaseName, ch, values)
|
||||
}
|
||||
// check if install/upgrade worked
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error when installing/upgrading Helm Chart %s in namespace %s: %w",
|
||||
releaseName, namespace, err)
|
||||
}
|
||||
if newRelease == nil {
|
||||
return nil, fmt.Errorf("failed to install release %s", releaseName)
|
||||
}
|
||||
return newRelease, nil
|
||||
}
|
||||
|
||||
// UninstallRelease uninstalls the provided release
|
||||
func (h *Helper) UninstallRelease(releaseName, namespace string, config *rest.Config, showDetail bool, logging cmdutil.IOStreams) error {
|
||||
cfg, err := newActionConfig(config, namespace, showDetail, logging)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iCli := action.NewUninstall(cfg)
|
||||
_, err = iCli.Run(releaseName)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error when uninstalling Helm release %s in namespace %s: %w",
|
||||
releaseName, namespace, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListVersions list available versions from repo
|
||||
func (h *Helper) ListVersions(repoURL string, chartName string) (repo.ChartVersions, error) {
|
||||
var body []byte
|
||||
if utils.IsValidURL(repoURL) {
|
||||
parsedURL, err := url.Parse(repoURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsedURL.RawPath = path.Join(parsedURL.RawPath, "index.yaml")
|
||||
parsedURL.Path = path.Join(parsedURL.Path, "index.yaml")
|
||||
indexURL := parsedURL.String()
|
||||
body, err = common.HTTPGet(context.Background(), indexURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("download index file from %s failure %w", repoURL, err)
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
body, err = ioutil.ReadFile(path.Join(filepath.Clean(repoURL), "index.yaml"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read index file from %s failure %w", repoURL, err)
|
||||
}
|
||||
}
|
||||
i := &repo.IndexFile{}
|
||||
if err := yaml.UnmarshalStrict(body, i); err != nil {
|
||||
return nil, fmt.Errorf("parse index file from %s failure %w", repoURL, err)
|
||||
}
|
||||
return i.Entries[chartName], nil
|
||||
}
|
||||
|
||||
// GetDeploymentsFromManifest get deployment from helm manifest
|
||||
func GetDeploymentsFromManifest(helmManifest string) []*appsv1.Deployment {
|
||||
deployments := []*appsv1.Deployment{}
|
||||
dec := kyaml.NewYAMLToJSONDecoder(strings.NewReader(helmManifest))
|
||||
for {
|
||||
var deployment appsv1.Deployment
|
||||
err := dec.Decode(&deployment)
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(deployment.Kind, "deployment") {
|
||||
deployments = append(deployments, &deployment)
|
||||
}
|
||||
}
|
||||
return deployments
|
||||
}
|
||||
|
||||
// GetCRDFromChart get crd from helm chart
|
||||
func GetCRDFromChart(chart *chart.Chart) []*crdv1.CustomResourceDefinition {
|
||||
crds := []*crdv1.CustomResourceDefinition{}
|
||||
for _, crdFile := range chart.CRDs() {
|
||||
var crd crdv1.CustomResourceDefinition
|
||||
err := kyaml.Unmarshal(crdFile.Data, &crd)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
crds = append(crds, &crd)
|
||||
}
|
||||
return crds
|
||||
}
|
||||
|
||||
func newActionConfig(config *rest.Config, namespace string, showDetail bool, logging cmdutil.IOStreams) (*action.Configuration, error) {
|
||||
restClientGetter := cmdutil.NewRestConfigGetterByConfig(config, namespace)
|
||||
log := func(format string, a ...interface{}) {
|
||||
if showDetail {
|
||||
logging.Infof(format+"\n", a...)
|
||||
}
|
||||
}
|
||||
kubeClient := &kube.Client{
|
||||
Factory: k8scmdutil.NewFactory(restClientGetter),
|
||||
Log: log,
|
||||
}
|
||||
client, err := kubeClient.Factory.KubernetesClientSet()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := driver.NewSecrets(client.CoreV1().Secrets(namespace))
|
||||
s.Log = log
|
||||
return &action.Configuration{
|
||||
RESTClientGetter: restClientGetter,
|
||||
Releases: storage.Init(s),
|
||||
KubeClient: kubeClient,
|
||||
Log: log,
|
||||
}, nil
|
||||
}
|
||||
68
pkg/utils/helm/helm_helper_test.go
Normal file
68
pkg/utils/helm/helm_helper_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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 helm
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Test helm helper", func() {
|
||||
|
||||
It("Test LoadCharts ", func() {
|
||||
helper := NewHelper()
|
||||
chart, err := helper.LoadCharts("./testdata/autoscalertrait-0.1.0.tgz")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(chart).ShouldNot(BeNil())
|
||||
Expect(chart.Metadata).ShouldNot(BeNil())
|
||||
Expect(cmp.Diff(chart.Metadata.Version, "0.1.0")).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test UpgradeChart", func() {
|
||||
helper := NewHelper()
|
||||
chart, err := helper.LoadCharts("./testdata/autoscalertrait-0.1.0.tgz")
|
||||
Expect(err).Should(BeNil())
|
||||
release, err := helper.UpgradeChart(chart, "autoscalertrait", "default", nil, UpgradeChartOptions{
|
||||
Config: cfg,
|
||||
Detail: false,
|
||||
Logging: util.IOStreams{Out: os.Stdout, ErrOut: os.Stderr},
|
||||
Wait: false,
|
||||
})
|
||||
crds := GetCRDFromChart(release.Chart)
|
||||
Expect(cmp.Diff(len(crds), 1)).Should(BeEmpty())
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test UninstallRelease", func() {
|
||||
helper := NewHelper()
|
||||
err := helper.UninstallRelease("autoscalertrait", "default", cfg, false, util.IOStreams{Out: os.Stdout, ErrOut: os.Stderr})
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test ListVersions ", func() {
|
||||
helper := NewHelper()
|
||||
versions, err := helper.ListVersions("./testdata", "autoscalertrait")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(versions), 2)).Should(BeEmpty())
|
||||
})
|
||||
|
||||
})
|
||||
63
pkg/utils/helm/helm_suite_test.go
Normal file
63
pkg/utils/helm/helm_suite_test.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 helm
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
)
|
||||
|
||||
var cfg *rest.Config
|
||||
var testEnv *envtest.Environment
|
||||
|
||||
var _ = BeforeSuite(func(done Done) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
By("bootstrapping test environment")
|
||||
|
||||
testEnv = &envtest.Environment{
|
||||
ControlPlaneStartTimeout: time.Minute * 3,
|
||||
ControlPlaneStopTimeout: time.Minute,
|
||||
UseExistingCluster: pointer.BoolPtr(false),
|
||||
}
|
||||
|
||||
By("start kube test env")
|
||||
var err error
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(cfg).ToNot(BeNil())
|
||||
|
||||
close(done)
|
||||
}, 240)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
if testEnv != nil {
|
||||
err := testEnv.Stop()
|
||||
Expect(err).Should(BeNil())
|
||||
}
|
||||
})
|
||||
|
||||
func TestHelm(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Helm Suite")
|
||||
}
|
||||
BIN
pkg/utils/helm/testdata/autoscalertrait-0.1.0.tgz
vendored
Normal file
BIN
pkg/utils/helm/testdata/autoscalertrait-0.1.0.tgz
vendored
Normal file
Binary file not shown.
24
pkg/utils/helm/testdata/index.yaml
vendored
Normal file
24
pkg/utils/helm/testdata/index.yaml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
apiVersion: v1
|
||||
entries:
|
||||
autoscalertrait:
|
||||
- apiVersion: v2
|
||||
appVersion: 1.16.0
|
||||
created: "2021-03-13T23:42:30.9837659+09:00"
|
||||
description: A Helm chart for kubevela autoscalertrait controller
|
||||
digest: 33c4b9eeabf6c26e6aa8c4edaaf0e4a5d2a2f1fd857ffe32b6d3be97d6d971f0
|
||||
name: autoscalertrait
|
||||
type: application
|
||||
urls:
|
||||
- https://charts.kubevela.net/example/autoscalertrait-0.1.0.tgz
|
||||
version: 0.1.0
|
||||
- apiVersion: v2
|
||||
appVersion: 1.16.0
|
||||
created: "2021-03-13T23:42:30.9837659+09:00"
|
||||
description: A Helm chart for kubevela autoscalertrait controller
|
||||
digest: 33c4b9eeabf6c26e6aa8c4edaaf0e4a5d2a2f1fd857ffe32b6d3be97d6d971f0
|
||||
name: autoscalertrait
|
||||
type: application
|
||||
urls:
|
||||
- https://charts.kubevela.net/example/autoscalertrait-0.1.0.tgz
|
||||
version: 0.2.0
|
||||
generated: "2021-03-13T23:42:30.9832644+09:00"
|
||||
@@ -18,6 +18,7 @@ package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
@@ -56,3 +57,16 @@ func ParseAPIServerEndpoint(server string) (string, error) {
|
||||
}
|
||||
return fmt.Sprintf("%s://%s:%s", scheme, host, port), nil
|
||||
}
|
||||
|
||||
// IsValidURL checks whether the given string is a valid URL or not
|
||||
func IsValidURL(strURL string) bool {
|
||||
_, err := url.ParseRequestURI(strURL)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
u, err := url.Parse(strURL)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -48,6 +48,14 @@ func NewRestConfigGetter(namespace string) genericclioptions.RESTClientGetter {
|
||||
}
|
||||
}
|
||||
|
||||
// NewRestConfigGetterByConfig new rest config getter
|
||||
func NewRestConfigGetterByConfig(config *rest.Config, namespace string) genericclioptions.RESTClientGetter {
|
||||
return &restConfigGetter{
|
||||
config: config,
|
||||
namespace: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
type restConfigGetter struct {
|
||||
config *rest.Config
|
||||
namespace string
|
||||
|
||||
@@ -18,9 +18,11 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
admissionv1 "k8s.io/api/admission/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
|
||||
@@ -67,6 +69,23 @@ func (h *ValidatingHandler) InjectDecoder(d *admission.Decoder) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func simplifyError(err error) error {
|
||||
switch e := err.(type) { // nolint
|
||||
case *field.Error:
|
||||
return fmt.Errorf("field \"%s\": %s error encountered, %s. ", e.Field, e.Type, e.Detail)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func mergeErrors(errs field.ErrorList) error {
|
||||
s := ""
|
||||
for _, err := range errs {
|
||||
s += fmt.Sprintf("field \"%s\": %s error encountered, %s. ", err.Field, err.Type, err.Detail)
|
||||
}
|
||||
return fmt.Errorf(s)
|
||||
}
|
||||
|
||||
// Handle validate Application Spec here
|
||||
func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
|
||||
app := &v1beta1.Application{}
|
||||
@@ -77,16 +96,16 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
|
||||
switch req.Operation {
|
||||
case admissionv1.Create:
|
||||
if allErrs := h.ValidateCreate(ctx, app); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, allErrs.ToAggregate())
|
||||
return admission.Errored(http.StatusUnprocessableEntity, mergeErrors(allErrs))
|
||||
}
|
||||
case admissionv1.Update:
|
||||
oldApp := &v1beta1.Application{}
|
||||
if err := h.Decoder.DecodeRaw(req.AdmissionRequest.OldObject, oldApp); err != nil {
|
||||
return admission.Errored(http.StatusBadRequest, err)
|
||||
return admission.Errored(http.StatusBadRequest, simplifyError(err))
|
||||
}
|
||||
if app.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
if allErrs := h.ValidateUpdate(ctx, app, oldApp); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, allErrs.ToAggregate())
|
||||
return admission.Errored(http.StatusUnprocessableEntity, mergeErrors(allErrs))
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1,53 +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 convert
|
||||
|
||||
import (
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
||||
wfContext "github.com/oam-dev/kubevela/pkg/workflow/context"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProviderName is provider name for install.
|
||||
ProviderName = "convert"
|
||||
)
|
||||
|
||||
type provider struct {
|
||||
}
|
||||
|
||||
// String convert byte to string
|
||||
func (h *provider) String(ctx wfContext.Context, v *value.Value, act types.Action) error {
|
||||
b, err := v.LookupValue("bt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s, err := b.CueValue().Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.FillObject(string(s), "str")
|
||||
}
|
||||
|
||||
// Install register handlers to provider discover.
|
||||
func Install(p providers.Providers) {
|
||||
prd := &provider{}
|
||||
p.Register(ProviderName, map[string]providers.Handler{
|
||||
"string": prd.String,
|
||||
})
|
||||
}
|
||||
@@ -1,73 +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 convert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers"
|
||||
)
|
||||
|
||||
func TestConvertString(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
from string
|
||||
expected string
|
||||
expectedErr error
|
||||
}{
|
||||
"success": {
|
||||
from: `bt: 'test'`,
|
||||
expected: "test",
|
||||
},
|
||||
"fail": {
|
||||
from: `bt: 123`,
|
||||
expectedErr: errors.New("bt: cannot use value 123 (type int) as string|bytes"),
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
r := require.New(t)
|
||||
v, err := value.NewValue(tc.from, nil, "")
|
||||
r.NoError(err)
|
||||
prd := &provider{}
|
||||
err = prd.String(nil, v, nil)
|
||||
if tc.expectedErr != nil {
|
||||
r.Equal(tc.expectedErr.Error(), err.Error())
|
||||
return
|
||||
}
|
||||
r.NoError(err)
|
||||
expected, err := v.LookupValue("str")
|
||||
r.NoError(err)
|
||||
ret, err := expected.CueValue().String()
|
||||
r.NoError(err)
|
||||
r.Equal(ret, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstall(t *testing.T) {
|
||||
p := providers.NewProviders()
|
||||
Install(p)
|
||||
h, ok := p.GetHandler("convert", "string")
|
||||
r := require.New(t)
|
||||
r.Equal(ok, true)
|
||||
r.Equal(h != nil, true)
|
||||
}
|
||||
@@ -91,6 +91,40 @@ func (h *provider) Apply(ctx wfContext.Context, v *value.Value, act types.Action
|
||||
return v.FillObject(workload.Object, "value")
|
||||
}
|
||||
|
||||
// ApplyInParallel create or update CRs in parallel.
|
||||
func (h *provider) ApplyInParallel(ctx wfContext.Context, v *value.Value, act types.Action) error {
|
||||
val, err := v.LookupValue("value")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iter, err := val.CueValue().List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
workloadNum := 0
|
||||
for iter.Next() {
|
||||
workloadNum++
|
||||
}
|
||||
var workloads = make([]*unstructured.Unstructured, workloadNum)
|
||||
if err = val.UnmarshalTo(&workloads); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range workloads {
|
||||
if workloads[i].GetNamespace() == "" {
|
||||
workloads[i].SetNamespace("default")
|
||||
}
|
||||
}
|
||||
cluster, err := v.GetString("cluster")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
deployCtx := multicluster.ContextWithClusterName(context.Background(), cluster)
|
||||
if err = h.apply(deployCtx, cluster, common.WorkflowResourceCreator, workloads...); err != nil {
|
||||
return v.FillObject(err, "err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read get CR from cluster.
|
||||
func (h *provider) Read(ctx wfContext.Context, v *value.Value, act types.Action) error {
|
||||
val, err := v.LookupValue("value")
|
||||
@@ -188,9 +222,10 @@ func Install(p providers.Providers, cli client.Client, apply Dispatcher, deleter
|
||||
cli: cli,
|
||||
}
|
||||
p.Register(ProviderName, map[string]providers.Handler{
|
||||
"apply": prd.Apply,
|
||||
"read": prd.Read,
|
||||
"list": prd.List,
|
||||
"delete": prd.Delete,
|
||||
"apply": prd.Apply,
|
||||
"apply-in-parallel": prd.ApplyInParallel,
|
||||
"read": prd.Read,
|
||||
"list": prd.List,
|
||||
"delete": prd.Delete,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -158,6 +158,7 @@ cluster: ""
|
||||
err = result.FillObject(expected.Object)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("patch & apply", func() {
|
||||
p := &provider{
|
||||
apply: func(ctx context.Context, _ string, _ common.ResourceCreatorRole, manifests ...*unstructured.Unstructured) error {
|
||||
@@ -409,7 +410,6 @@ val: {
|
||||
err = p.Apply(ctx, v, nil)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
func newWorkflowContextForTest() (wfContext.Context, error) {
|
||||
|
||||
@@ -20,13 +20,12 @@ import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/sets"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"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/cue/model/sets"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
wfContext "github.com/oam-dev/kubevela/pkg/workflow/context"
|
||||
|
||||
82
pkg/workflow/providers/util/util.go
Normal file
82
pkg/workflow/providers/util/util.go
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2022. The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
||||
wfContext "github.com/oam-dev/kubevela/pkg/workflow/context"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProviderName is provider name for install.
|
||||
ProviderName = "util"
|
||||
)
|
||||
|
||||
type provider struct{}
|
||||
|
||||
func (p *provider) PatchK8sObject(ctx wfContext.Context, v *value.Value, act types.Action) error {
|
||||
val, err := v.LookupValue("value")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pv, err := v.LookupValue("patch")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
base, err := model.NewBase(val.CueValue())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
patcher, err := model.NewOther(pv.CueValue())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = base.Unify(patcher); err != nil {
|
||||
return v.FillObject(err, "err")
|
||||
}
|
||||
|
||||
workload, err := base.Unstructured()
|
||||
if err != nil {
|
||||
return v.FillObject(err, "err")
|
||||
}
|
||||
return v.FillObject(workload.Object, "result")
|
||||
}
|
||||
|
||||
// String convert byte to string
|
||||
func (p *provider) String(ctx wfContext.Context, v *value.Value, act types.Action) error {
|
||||
b, err := v.LookupValue("bt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s, err := b.CueValue().Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.FillObject(string(s), "str")
|
||||
}
|
||||
|
||||
// Install register handlers to provider discover.
|
||||
func Install(p providers.Providers) {
|
||||
prd := &provider{}
|
||||
p.Register(ProviderName, map[string]providers.Handler{
|
||||
"patch-k8s-object": prd.PatchK8sObject,
|
||||
"string": prd.String,
|
||||
})
|
||||
}
|
||||
202
pkg/workflow/providers/util/util_test.go
Normal file
202
pkg/workflow/providers/util/util_test.go
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
Copyright 2022. The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/cue/model/value"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers"
|
||||
)
|
||||
|
||||
func TestPatchK8sObject(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
value string
|
||||
expectedErr error
|
||||
patchResult string
|
||||
}{
|
||||
"test patch k8s object": {
|
||||
value: `
|
||||
value: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: template: metadata: {
|
||||
labels: {
|
||||
"oam.dev/name": "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
patch: {
|
||||
spec: template: metadata: {
|
||||
labels: {
|
||||
"test-label": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
expectedErr: nil,
|
||||
patchResult: `
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: template: metadata: {
|
||||
labels: {
|
||||
"oam.dev/name": "test"
|
||||
"test-label": "true"
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
"test patch k8s object with patchKey": {
|
||||
value: `
|
||||
value: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: template: spec: {
|
||||
containers: [{
|
||||
name: "test"
|
||||
}]
|
||||
}
|
||||
}
|
||||
patch: {
|
||||
spec: template: spec: {
|
||||
// +patchKey=name
|
||||
containers: [{
|
||||
name: "test"
|
||||
env: [{
|
||||
name: "test-env"
|
||||
value: "test-value"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
`,
|
||||
expectedErr: nil,
|
||||
patchResult: `
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: template: spec: {
|
||||
containers: [{
|
||||
name: "test"
|
||||
env: [{
|
||||
name: "test-env"
|
||||
value: "test-value"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
`,
|
||||
},
|
||||
"test patch k8s object with patchStrategy": {
|
||||
value: `
|
||||
value: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: template: metadata: {
|
||||
name: "test-name"
|
||||
}
|
||||
}
|
||||
patch: {
|
||||
// +patchStrategy=retainKeys
|
||||
spec: template: metadata: {
|
||||
name: "test-patchStrategy"
|
||||
}
|
||||
}
|
||||
`,
|
||||
expectedErr: nil,
|
||||
patchResult: `
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: template: metadata: {
|
||||
name: "test-patchStrategy"
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
r := require.New(t)
|
||||
v, err := value.NewValue(tc.value, nil, "")
|
||||
r.NoError(err)
|
||||
prd := &provider{}
|
||||
err = prd.PatchK8sObject(nil, v, nil)
|
||||
if tc.expectedErr != nil {
|
||||
r.Equal(tc.expectedErr.Error(), err.Error())
|
||||
return
|
||||
}
|
||||
r.NoError(err)
|
||||
result, err := v.LookupValue("result")
|
||||
r.NoError(err)
|
||||
var patchResult map[string]interface{}
|
||||
r.NoError(result.UnmarshalTo(&patchResult))
|
||||
var expectResult map[string]interface{}
|
||||
resultValue, err := value.NewValue(tc.patchResult, nil, "")
|
||||
r.NoError(err)
|
||||
r.NoError(resultValue.UnmarshalTo(&expectResult))
|
||||
assert.Equal(t, expectResult, patchResult)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertString(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
from string
|
||||
expected string
|
||||
expectedErr error
|
||||
}{
|
||||
"success": {
|
||||
from: `bt: 'test'`,
|
||||
expected: "test",
|
||||
},
|
||||
"fail": {
|
||||
from: `bt: 123`,
|
||||
expectedErr: errors.New("bt: cannot use value 123 (type int) as string|bytes"),
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
r := require.New(t)
|
||||
v, err := value.NewValue(tc.from, nil, "")
|
||||
r.NoError(err)
|
||||
prd := &provider{}
|
||||
err = prd.String(nil, v, nil)
|
||||
if tc.expectedErr != nil {
|
||||
r.Equal(tc.expectedErr.Error(), err.Error())
|
||||
return
|
||||
}
|
||||
r.NoError(err)
|
||||
expected, err := v.LookupValue("str")
|
||||
r.NoError(err)
|
||||
ret, err := expected.CueValue().String()
|
||||
r.NoError(err)
|
||||
r.Equal(ret, tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstall(t *testing.T) {
|
||||
p := providers.NewProviders()
|
||||
Install(p)
|
||||
h, ok := p.GetHandler("util", "string")
|
||||
r := require.New(t)
|
||||
r.Equal(ok, true)
|
||||
r.Equal(h != nil, true)
|
||||
}
|
||||
@@ -30,11 +30,11 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/velaql/providers/query"
|
||||
wfContext "github.com/oam-dev/kubevela/pkg/workflow/context"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers/convert"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers/email"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers/http"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers/kube"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers/time"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers/util"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/providers/workspace"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/tasks/custom"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/tasks/template"
|
||||
@@ -77,8 +77,9 @@ func suspend(step v1beta1.WorkflowStep, opt *types.GeneratorOptions) (types.Task
|
||||
func NewTaskDiscover(providerHandlers providers.Providers, pd *packages.PackageDiscover, cli client.Client, dm discoverymapper.DiscoveryMapper) types.TaskDiscover {
|
||||
// install builtin provider
|
||||
workspace.Install(providerHandlers)
|
||||
convert.Install(providerHandlers)
|
||||
email.Install(providerHandlers)
|
||||
util.Install(providerHandlers)
|
||||
|
||||
templateLoader := template.NewWorkflowStepTemplateLoader(cli, dm)
|
||||
return &taskDiscover{
|
||||
builtins: map[string]types.TaskGenerator{
|
||||
@@ -123,7 +124,6 @@ func NewViewTaskDiscover(pd *packages.PackageDiscover, cli client.Client, cfg *r
|
||||
time.Install(handlerProviders)
|
||||
kube.Install(handlerProviders, cli, apply, delete)
|
||||
http.Install(handlerProviders, cli, viewNs)
|
||||
convert.Install(handlerProviders)
|
||||
email.Install(handlerProviders)
|
||||
|
||||
templateLoader := template.NewViewTemplateLoader(cli, viewNs)
|
||||
|
||||
@@ -22,16 +22,20 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
gov "github.com/hashicorp/go-version"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/helm"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/system"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
var assumeYes bool
|
||||
|
||||
// NewCommand will contain all commands
|
||||
func NewCommand() *cobra.Command {
|
||||
ioStream := util.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
|
||||
@@ -45,7 +49,6 @@ func NewCommand() *cobra.Command {
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeStart)
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeApp)
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeCD)
|
||||
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeExtension)
|
||||
PrintHelpByTag(cmd, allCommands, types.TypeSystem)
|
||||
cmd.Println("Flags:")
|
||||
@@ -98,10 +101,12 @@ func NewCommand() *cobra.Command {
|
||||
NewComponentsCommand(commandArgs, ioStream),
|
||||
|
||||
// System
|
||||
NewInstallCommand(commandArgs, "1", ioStream),
|
||||
NewUnInstallCommand(commandArgs, "2", ioStream),
|
||||
NewExportCommand(commandArgs, ioStream),
|
||||
NewCUEPackageCommand(commandArgs, ioStream),
|
||||
SystemCommandGroup(commandArgs, ioStream),
|
||||
NewVersionCommand(),
|
||||
NewVersionCommand(ioStream),
|
||||
NewCompletionCommand(),
|
||||
|
||||
// helper
|
||||
@@ -117,12 +122,14 @@ func NewCommand() *cobra.Command {
|
||||
klog.InitFlags(fset)
|
||||
_ = fset.Set("v", "-1")
|
||||
|
||||
// init global flags
|
||||
cmds.PersistentFlags().BoolVarP(&assumeYes, "yes", "y", false, "Assume yes for all user prompts")
|
||||
return cmds
|
||||
}
|
||||
|
||||
// NewVersionCommand print client version
|
||||
func NewVersionCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
func NewVersionCommand(ioStream util.IOStreams) *cobra.Command {
|
||||
version := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Prints vela build version information",
|
||||
Long: "Prints vela build version information.",
|
||||
@@ -139,4 +146,47 @@ GolangVersion: %v
|
||||
types.TagCommandType: types.TypeSystem,
|
||||
},
|
||||
}
|
||||
version.AddCommand(NewVersionListCommand(ioStream))
|
||||
return version
|
||||
}
|
||||
|
||||
// NewVersionListCommand show all versions command
|
||||
func NewVersionListCommand(ioStream util.IOStreams) *cobra.Command {
|
||||
var showAll bool
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List all available versions",
|
||||
Long: "Query all available versions from remote server.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
helmHelper := helm.NewHelper()
|
||||
versions, err := helmHelper.ListVersions(kubevelaInstallerHelmRepoURL, kubeVelaChartName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentV, err := gov.NewVersion(version.VelaVersion)
|
||||
if err != nil && !showAll {
|
||||
return fmt.Errorf("can not parse current version %s", version.VelaVersion)
|
||||
}
|
||||
for _, chartV := range versions {
|
||||
if chartV != nil {
|
||||
v, err := gov.NewVersion(chartV.Version)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if v.GreaterThan(currentV) {
|
||||
ioStream.Info("Newer Version:", v.String())
|
||||
} else if showAll {
|
||||
ioStream.Info("Older Version:", v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
types.TagCommandType: types.TypeSystem,
|
||||
},
|
||||
}
|
||||
cmd.PersistentFlags().BoolVarP(&showAll, "all", "a", false, "List all available versions, if not, only list newer version")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -74,3 +74,10 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).Should(BeNil())
|
||||
close(done)
|
||||
}, 240)
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
if testEnv != nil {
|
||||
err := testEnv.Stop()
|
||||
Expect(err).Should(BeNil())
|
||||
}
|
||||
})
|
||||
|
||||
@@ -279,7 +279,7 @@ func NewDefinitionInitCommand(c common.Args) *cobra.Command {
|
||||
}
|
||||
cmd.Flags().StringP(FlagType, "t", "", "Specify the type of the new definition. Valid types: "+strings.Join(pkgdef.ValidDefinitionTypes(), ", "))
|
||||
cmd.Flags().StringP(FlagDescription, "d", "", "Specify the description of the new definition.")
|
||||
cmd.Flags().StringP(FlagTemplateYAML, "y", "", "Specify the template yaml file that definition will use to build the schema. If empty, a default template for the given definition type will be used.")
|
||||
cmd.Flags().StringP(FlagTemplateYAML, "f", "", "Specify the template yaml file that definition will use to build the schema. If empty, a default template for the given definition type will be used.")
|
||||
cmd.Flags().StringP(FlagOutput, "o", "", "Specify the output path of the generated definition. If empty, the definition will be printed in the console.")
|
||||
cmd.Flags().BoolP(FlagInteractive, "i", false, "Specify whether use interactive process to help generate definitions.")
|
||||
cmd.Flags().StringP(FlagProvider, "p", "", "Specify which provider the cloud resource definition belongs to. Only `alibaba`, `aws`, `azure` are supported.")
|
||||
|
||||
323
references/cli/install.go
Normal file
323
references/cli/install.go
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"cuelang.org/go/pkg/strings"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/strvals"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierror "k8s.io/apimachinery/pkg/api/errors"
|
||||
apitypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/helm"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
innerVersion "github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// defaultConstraint
|
||||
const defaultConstraint = ">= 1.19, <= 1.21"
|
||||
|
||||
const kubevelaInstallerHelmRepoURL = "https://charts.kubevela.net/core/"
|
||||
|
||||
// kubeVelaReleaseName release name
|
||||
const kubeVelaReleaseName = "kubevela"
|
||||
|
||||
// kubeVelaChartName the name of veal core chart
|
||||
const kubeVelaChartName = "vela-core"
|
||||
|
||||
// InstallArgs the args for install command
|
||||
type InstallArgs struct {
|
||||
userInput *UserInput
|
||||
helmHelper *helm.Helper
|
||||
Args common.Args
|
||||
Values []string
|
||||
Namespace string
|
||||
Version string
|
||||
ChartFilePath string
|
||||
Detail bool
|
||||
ReuseValues bool
|
||||
}
|
||||
|
||||
// NewInstallCommand creates `install` command to install vela core
|
||||
func NewInstallCommand(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command {
|
||||
installArgs := &InstallArgs{Args: c, userInput: NewUserInput(), helmHelper: helm.NewHelper()}
|
||||
cmd := &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "Installs or Upgrades Kubevela control plane on a Kubernetes cluster.",
|
||||
Long: "The Kubevela CLI allows installing Kubevela on any Kubernetes derivative to which your kube config is pointing to.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
// CheckRequirements
|
||||
ioStreams.Info("Check Requirements ...")
|
||||
restConfig, err := c.GetConfig()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get kube config, You can set KUBECONFIG env or make file ~/.kube/config")
|
||||
}
|
||||
if isNewerVersion, serverVersion, err := checkKubeServerVersion(restConfig); err != nil {
|
||||
ioStreams.Error(err.Error())
|
||||
ioStreams.Error("This is not recommended and could have negative impacts on the stability of KubeVela - use at your own risk.")
|
||||
|
||||
userConfirmation := installArgs.userInput.AskBool("Do you want to continue?", &UserInputOptions{assumeYes})
|
||||
if !userConfirmation {
|
||||
return fmt.Errorf("stopping installation")
|
||||
}
|
||||
} else if isNewerVersion {
|
||||
ioStreams.Errorf("The Kubernetes server version(%s) is higher than the one officially supported(%s).\n", serverVersion, defaultConstraint)
|
||||
ioStreams.Error("This is not recommended and could have negative impacts on the stability of KubeVela - use at your own risk.")
|
||||
userInput := NewUserInput()
|
||||
userConfirmation := userInput.AskBool("Do you want to continue?", &UserInputOptions{assumeYes})
|
||||
if !userConfirmation {
|
||||
return fmt.Errorf("stopping installation")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Step1: Download Helm Chart
|
||||
ioStreams.Info("Installing KubeVela Core ...")
|
||||
if installArgs.ChartFilePath == "" {
|
||||
installArgs.ChartFilePath = getKubeVelaHelmChartRepoURL(installArgs.Version)
|
||||
}
|
||||
chart, err := installArgs.helmHelper.LoadCharts(installArgs.ChartFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loadding the helm chart of kubeVela control plane failure, %w", err)
|
||||
}
|
||||
ioStreams.Infof("Helm Chart used for KubeVela control plane installation: %s \n", installArgs.ChartFilePath)
|
||||
|
||||
// Step2: Prepare namespace
|
||||
restConfig, err := c.GetConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get kube config failure: %w", err)
|
||||
}
|
||||
kubeClient, err := c.GetClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("create kube client failure: %w", err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||
defer cancel()
|
||||
var namespace corev1.Namespace
|
||||
var namespaceExists = true
|
||||
if err := kubeClient.Get(ctx, apitypes.NamespacedName{Name: installArgs.Namespace}, &namespace); err != nil {
|
||||
if !apierror.IsNotFound(err) {
|
||||
return fmt.Errorf("failed to check if namespace %s already exists: %w", installArgs.Namespace, err)
|
||||
}
|
||||
namespaceExists = false
|
||||
}
|
||||
if namespaceExists {
|
||||
fmt.Printf("Existing KubeVela installation found in namespace %s\n\n", installArgs.Namespace)
|
||||
userConfirmation := installArgs.userInput.AskBool("Do you want to overwrite this installation?", &UserInputOptions{assumeYes})
|
||||
if !userConfirmation {
|
||||
return fmt.Errorf("stopping installation")
|
||||
}
|
||||
} else {
|
||||
namespace.Name = installArgs.Namespace
|
||||
if err := kubeClient.Create(ctx, &namespace); err != nil {
|
||||
return fmt.Errorf("failed to create kubeVela namespace %s: %w", installArgs.Namespace, err)
|
||||
}
|
||||
}
|
||||
// Step3: Prepare the values for chart
|
||||
imageTag := installArgs.Version
|
||||
if !strings.HasPrefix(imageTag, "v") {
|
||||
imageTag = "v" + imageTag
|
||||
}
|
||||
var values = map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": imageTag,
|
||||
"pullPolicy": "IfNotPresent",
|
||||
},
|
||||
}
|
||||
if len(installArgs.Values) > 0 {
|
||||
for _, value := range installArgs.Values {
|
||||
if err := strvals.ParseInto(value, values); err != nil {
|
||||
return errors.Wrap(err, "failed parsing --set data")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Step4: apply new CRDs
|
||||
if err := upgradeCRDs(cmd.Context(), kubeClient, chart); err != nil {
|
||||
return errors.New(fmt.Sprintf("upgrade CRD failure %s", err.Error()))
|
||||
}
|
||||
// Step5: Install or upgrade helm release
|
||||
release, err := installArgs.helmHelper.UpgradeChart(chart, kubeVelaReleaseName, installArgs.Namespace, values,
|
||||
helm.UpgradeChartOptions{
|
||||
Config: restConfig,
|
||||
Detail: installArgs.Detail,
|
||||
Logging: ioStreams,
|
||||
Wait: true,
|
||||
ReuseValues: installArgs.ReuseValues,
|
||||
})
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Could not install KubeVela control plane installation: %s", err.Error())
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
err = waitKubeVelaControllerRunning(kubeClient, installArgs.Namespace, release.Manifest)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Could not complete KubeVela control plane installation: %s \nFor troubleshooting, please check the status of the kubevela deployment by executing the following command: \n\nkubectl get pods -n %s\n", err.Error(), installArgs.Namespace)
|
||||
return errors.New(msg)
|
||||
}
|
||||
ioStreams.Info()
|
||||
ioStreams.Info("KubeVela control plane has been successfully set up on your cluster.")
|
||||
ioStreams.Info("If you want to enable dashboard, please run \"vela addon enable velaux\"")
|
||||
return nil
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
types.TagCommandOrder: order,
|
||||
types.TagCommandType: types.TypeSystem,
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringArrayVarP(&installArgs.Values, "set", "", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
cmd.Flags().StringVarP(&installArgs.Namespace, "namespace", "n", "vela-system", "namespace scope for installing KubeVela Core")
|
||||
cmd.Flags().StringVarP(&installArgs.Version, "version", "v", innerVersion.VelaVersion, "")
|
||||
cmd.Flags().BoolVarP(&installArgs.Detail, "detail", "d", true, "show detail log of installation")
|
||||
cmd.Flags().BoolVarP(&installArgs.ReuseValues, "reuse", "r", true, "will re-use the user's last supplied values.")
|
||||
cmd.Flags().StringVarP(&installArgs.ChartFilePath, "file", "f", "", "custom the chart path of KubeVela control plane")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func checkKubeServerVersion(config *rest.Config) (bool, string, error) {
|
||||
// get kubernetes cluster api version
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
// check version
|
||||
serverVersion, err := client.ServerVersion()
|
||||
if err != nil {
|
||||
return false, "", fmt.Errorf("get kubernetes api version failure %w", err)
|
||||
}
|
||||
vStr := fmt.Sprintf("%s.%s", serverVersion.Major, strings.Replace(serverVersion.Minor, "+", "", 1))
|
||||
currentVersion, err := version.NewVersion(vStr)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
hConstraints, err := version.NewConstraint(defaultConstraint)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
isNewerVersion, allConstraintsValid := checkIsNewVersion(hConstraints, currentVersion)
|
||||
|
||||
if allConstraintsValid {
|
||||
return false, vStr, nil
|
||||
}
|
||||
if isNewerVersion {
|
||||
return true, vStr, nil
|
||||
}
|
||||
|
||||
return false, vStr, fmt.Errorf("the kubernetes server version '%s' doesn't satisfy constraints '%s'", serverVersion, defaultConstraint)
|
||||
}
|
||||
|
||||
// checkIsNewVersion checks if the provided version is higher than all constraints and if all constraints are valid
|
||||
func checkIsNewVersion(hConstraints version.Constraints, serverVersion *version.Version) (bool, bool) {
|
||||
isNewerVersion := false
|
||||
allConstraintsValid := true
|
||||
for _, constraint := range hConstraints {
|
||||
validConstraint := constraint.Check(serverVersion)
|
||||
if !validConstraint {
|
||||
allConstraintsValid = false
|
||||
constraintVersionString := getConstraintVersion(constraint.String())
|
||||
constraintVersion, err := version.NewVersion(constraintVersionString)
|
||||
if err != nil {
|
||||
return false, false
|
||||
}
|
||||
if serverVersion.GreaterThan(constraintVersion) {
|
||||
isNewerVersion = true
|
||||
} else {
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
}
|
||||
return isNewerVersion, allConstraintsValid
|
||||
}
|
||||
|
||||
// getConstraintVersion returns the version of a constraint without leading spaces, <, >, =
|
||||
func getConstraintVersion(constraint string) string {
|
||||
for index, character := range constraint {
|
||||
if character != '<' && character != '>' && character != ' ' && character != '=' {
|
||||
return constraint[index:]
|
||||
}
|
||||
}
|
||||
return constraint
|
||||
}
|
||||
|
||||
func getKubeVelaHelmChartRepoURL(version string) string {
|
||||
// Determine installer version
|
||||
if innerVersion.IsOfficialKubeVelaVersion(version) {
|
||||
version, _ := innerVersion.GetOfficialKubeVelaVersion(version)
|
||||
return kubevelaInstallerHelmRepoURL + kubeVelaChartName + "-" + version + ".tgz"
|
||||
}
|
||||
return kubevelaInstallerHelmRepoURL + kubeVelaChartName + "-" + version + ".tgz"
|
||||
}
|
||||
|
||||
func waitKubeVelaControllerRunning(kubeClient client.Client, namespace, manifest string) error {
|
||||
deployments := helm.GetDeploymentsFromManifest(manifest)
|
||||
spinner := newTrackingSpinnerWithDelay("Waiting KubeVela control plane running ...", 1*time.Second)
|
||||
spinner.Start()
|
||||
defer spinner.Stop()
|
||||
trackInterval := 5 * time.Second
|
||||
timeout := 600 * time.Second
|
||||
start := time.Now()
|
||||
ctx := context.Background()
|
||||
for {
|
||||
timeConsumed := int(time.Since(start).Seconds())
|
||||
var readyCount = 0
|
||||
for i, d := range deployments {
|
||||
err := kubeClient.Get(ctx, apitypes.NamespacedName{Name: d.Name, Namespace: namespace}, deployments[i])
|
||||
if err != nil {
|
||||
return client.IgnoreNotFound(err)
|
||||
}
|
||||
if deployments[i].Status.ReadyReplicas != deployments[i].Status.Replicas {
|
||||
applySpinnerNewSuffix(spinner, fmt.Sprintf("Waiting deployment %s ready. (timeout %d/%d seconds)...", deployments[i].Name, timeConsumed, int(timeout.Seconds())))
|
||||
} else {
|
||||
readyCount++
|
||||
}
|
||||
}
|
||||
if readyCount >= len(deployments) {
|
||||
return nil
|
||||
}
|
||||
if timeConsumed > int(timeout.Seconds()) {
|
||||
return errors.Errorf("Enabling timeout, please run \"kubectl get pod -n vela-system\" to check the status")
|
||||
}
|
||||
time.Sleep(trackInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func upgradeCRDs(ctx context.Context, kubeClient client.Client, chart *chart.Chart) error {
|
||||
crds := helm.GetCRDFromChart(chart)
|
||||
apply := apply.NewAPIApplicator(kubeClient)
|
||||
for _, crd := range crds {
|
||||
if err := apply.Apply(ctx, crd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
38
references/cli/install_test.go
Normal file
38
references/cli/install_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetKubeVelaHelmChartRepoURL(t *testing.T) {
|
||||
assert.Equal(t, getKubeVelaHelmChartRepoURL("v1.2.2"), "https://charts.kubevela.net/core/vela-core-1.2.2.tgz")
|
||||
assert.Equal(t, getKubeVelaHelmChartRepoURL("1.1.11"), "https://charts.kubevela.net/core/vela-core-1.1.11.tgz")
|
||||
}
|
||||
|
||||
var _ = Describe("Test Install Command", func() {
|
||||
It("Test checkKubeServerVersion", func() {
|
||||
new, _, err := checkKubeServerVersion(cfg)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(new).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
@@ -30,6 +30,9 @@ import (
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
// AllNamespace list app in all namespaces
|
||||
var AllNamespace bool
|
||||
|
||||
// NewListCommand creates `ls` command and its nested children command
|
||||
func NewListCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
ctx := context.Background()
|
||||
@@ -49,6 +52,9 @@ func NewListCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *c
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if AllNamespace {
|
||||
namespace = ""
|
||||
}
|
||||
return printApplicationList(ctx, newClient, namespace, ioStreams)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
@@ -57,12 +63,17 @@ func NewListCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *c
|
||||
},
|
||||
}
|
||||
addNamespaceAndEnvArg(cmd)
|
||||
cmd.Flags().BoolVarP(&AllNamespace, "all-namespaces", "A", false, "If true, check the specified action in all namespaces.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func printApplicationList(ctx context.Context, c client.Reader, namespace string, ioStreams cmdutil.IOStreams) error {
|
||||
table := newUITable()
|
||||
table.AddRow("APP", "COMPONENT", "TYPE", "TRAITS", "PHASE", "HEALTHY", "STATUS", "CREATED-TIME")
|
||||
header := []interface{}{"APP", "COMPONENT", "TYPE", "TRAITS", "PHASE", "HEALTHY", "STATUS", "CREATED-TIME"}
|
||||
if AllNamespace {
|
||||
header = append([]interface{}{"NAMESPACE"}, header...)
|
||||
}
|
||||
table.AddRow(header...)
|
||||
applist := v1beta1.ApplicationList{}
|
||||
if err := c.List(ctx, &applist, client.InNamespace(namespace)); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
@@ -94,7 +105,11 @@ func printApplicationList(ctx context.Context, c client.Reader, namespace string
|
||||
for _, tr := range cmp.Traits {
|
||||
traits = append(traits, tr.Type)
|
||||
}
|
||||
table.AddRow(appName, cmp.Name, cmp.Type, strings.Join(traits, ","), a.Status.Phase, healthy, status, a.CreationTimestamp)
|
||||
if AllNamespace {
|
||||
table.AddRow(a.Namespace, appName, cmp.Name, cmp.Type, strings.Join(traits, ","), a.Status.Phase, healthy, status, a.CreationTimestamp)
|
||||
} else {
|
||||
table.AddRow(appName, cmp.Name, cmp.Type, strings.Join(traits, ","), a.Status.Phase, healthy, status, a.CreationTimestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
ioStreams.Info(table.String())
|
||||
|
||||
129
references/cli/uninstall.go
Normal file
129
references/cli/uninstall.go
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierror "k8s.io/apimachinery/pkg/api/errors"
|
||||
apitypes "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/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/helm"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
// UnInstallArgs the args for uninstall command
|
||||
type UnInstallArgs struct {
|
||||
userInput *UserInput
|
||||
helmHelper *helm.Helper
|
||||
Args common.Args
|
||||
Namespace string
|
||||
Detail bool
|
||||
}
|
||||
|
||||
// NewUnInstallCommand creates `uninstall` command to uninstall vela core
|
||||
func NewUnInstallCommand(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command {
|
||||
unInstallArgs := &UnInstallArgs{Args: c, userInput: NewUserInput(), helmHelper: helm.NewHelper()}
|
||||
cmd := &cobra.Command{
|
||||
Use: "uninstall",
|
||||
Short: "Uninstalls KubeVela from a Kubernetes cluster",
|
||||
Example: `vela uninstall`,
|
||||
Long: "Uninstalls KubeVela from a Kubernetes cluster.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
userConfirmation := unInstallArgs.userInput.AskBool("Would you like to uninstall KubeVela from this cluster?", &UserInputOptions{AssumeYes: assumeYes})
|
||||
if !userConfirmation {
|
||||
return nil
|
||||
}
|
||||
kubeClient, err := c.GetClient()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get kube client")
|
||||
}
|
||||
var apps v1beta1.ApplicationList
|
||||
err = kubeClient.List(context.Background(), &apps, &client.ListOptions{
|
||||
Namespace: "",
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to check app in cluster")
|
||||
}
|
||||
if len(apps.Items) > 0 {
|
||||
return fmt.Errorf("please delete all applications before uninstall. using \"vela ls -A\" view all applications")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ioStreams.Info("Starting to uninstall KubeVela")
|
||||
restConfig, err := c.GetConfig()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get kube config, You can set KUBECONFIG env or make file ~/.kube/config")
|
||||
}
|
||||
if err := unInstallArgs.helmHelper.UninstallRelease(kubeVelaReleaseName, unInstallArgs.Namespace, restConfig, unInstallArgs.Detail, ioStreams); err != nil {
|
||||
return err
|
||||
}
|
||||
// Clean up vela-system namespace
|
||||
kubeClient, err := c.GetClient()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get kube client")
|
||||
}
|
||||
if err := deleteNamespace(kubeClient, unInstallArgs.Namespace); err != nil {
|
||||
return err
|
||||
}
|
||||
var namespace corev1.Namespace
|
||||
var namespaceExists = true
|
||||
if err := kubeClient.Get(cmd.Context(), apitypes.NamespacedName{Name: "kubevela"}, &namespace); err != nil {
|
||||
if !apierror.IsNotFound(err) {
|
||||
return fmt.Errorf("failed to check if namespace kubevela already exists: %w", err)
|
||||
}
|
||||
namespaceExists = false
|
||||
}
|
||||
if namespaceExists {
|
||||
fmt.Printf("The namespace kubevela is exist, it is the default database of the velaux\n\n")
|
||||
userConfirmation := unInstallArgs.userInput.AskBool("Do you want to delete it?", &UserInputOptions{assumeYes})
|
||||
if userConfirmation {
|
||||
if err := deleteNamespace(kubeClient, "kubevela"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
ioStreams.Info("Successfully uninstalled KubeVela")
|
||||
ioStreams.Info("Please delete all CRD from cluster using \"kubectl get crd |grep oam | awk '{print $1}' | xargs kubectl delete crd\"")
|
||||
return nil
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
types.TagCommandOrder: order,
|
||||
types.TagCommandType: types.TypeSystem,
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&unInstallArgs.Namespace, "namespace", "n", "vela-system", "namespace scope for installing KubeVela Core")
|
||||
cmd.Flags().BoolVarP(&unInstallArgs.Detail, "detail", "d", true, "show detail log of installation")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func deleteNamespace(kubeClient client.Client, namespace string) error {
|
||||
var ns corev1.Namespace
|
||||
ns.Name = namespace
|
||||
return kubeClient.Delete(context.Background(), &ns)
|
||||
}
|
||||
@@ -17,10 +17,15 @@ limitations under the License.
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -66,3 +71,47 @@ func getCompNameFromClusterObjectReference(ctx context.Context, k8sClient client
|
||||
}
|
||||
return labels[oam.LabelAppComponent], nil
|
||||
}
|
||||
|
||||
// UserInput user input in command
|
||||
type UserInput struct {
|
||||
Writer io.Writer
|
||||
Reader *bufio.Reader
|
||||
}
|
||||
|
||||
// UserInputOptions user input options
|
||||
type UserInputOptions struct {
|
||||
AssumeYes bool
|
||||
}
|
||||
|
||||
// NewUserInput new user input util
|
||||
func NewUserInput() *UserInput {
|
||||
return &UserInput{
|
||||
Writer: os.Stdout,
|
||||
Reader: bufio.NewReader(os.Stdin),
|
||||
}
|
||||
}
|
||||
|
||||
// AskBool format the answer to bool type
|
||||
func (ui *UserInput) AskBool(question string, opts *UserInputOptions) bool {
|
||||
fmt.Fprintf(ui.Writer, "%s (y/n)", question)
|
||||
if opts.AssumeYes {
|
||||
return true
|
||||
}
|
||||
line, err := ui.read()
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
if input := strings.TrimSpace(strings.ToLower(line)); input == "y" || input == "yes" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ui *UserInput) read() (string, error) {
|
||||
line, err := ui.Reader.ReadString('\n')
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return "", err
|
||||
}
|
||||
resultStr := strings.TrimSuffix(line, "\n")
|
||||
return resultStr, err
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/meta"
|
||||
@@ -43,6 +42,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcekeeper"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
@@ -256,7 +256,7 @@ func ReadRemoteOrLocalPath(pathOrURL string) ([]byte, error) {
|
||||
}
|
||||
var body []byte
|
||||
var err error
|
||||
if strings.HasPrefix(pathOrURL, "https://") || strings.HasPrefix(pathOrURL, "http://") {
|
||||
if utils.IsValidURL(pathOrURL) {
|
||||
body, err = common.HTTPGet(context.Background(), pathOrURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Build the manager binary
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.16-alpine as builder
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.17-alpine as builder
|
||||
|
||||
WORKDIR /workspace
|
||||
# Copy the Go Modules manifests
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Build the manager binary
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.16-alpine as builder
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.17-alpine as builder
|
||||
|
||||
WORKDIR /workspace
|
||||
# Copy the Go Modules manifests
|
||||
|
||||
@@ -16,8 +16,31 @@ limitations under the License.
|
||||
|
||||
package version
|
||||
|
||||
import "github.com/hashicorp/go-version"
|
||||
|
||||
// GitRevision is the commit of repo
|
||||
var GitRevision = "UNKNOWN"
|
||||
|
||||
// VelaVersion is the version of cli.
|
||||
var VelaVersion = "UNKNOWN"
|
||||
|
||||
// IsOfficialKubeVelaVersion checks whether the provided version string follows a KubeVela version pattern
|
||||
func IsOfficialKubeVelaVersion(versionStr string) bool {
|
||||
_, err := version.NewSemver(versionStr)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GetOfficialKubeVelaVersion extracts the KubeVela version from the provided string
|
||||
// More precisely, this method returns the segments and prerelease info w/o metadata
|
||||
func GetOfficialKubeVelaVersion(versionStr string) (string, error) {
|
||||
s, err := version.NewSemver(versionStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
v := s.String()
|
||||
metadata := s.Metadata()
|
||||
if metadata != "" {
|
||||
metadata = "+" + metadata
|
||||
}
|
||||
return v[:len(v)-len(metadata)], nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user