Compare commits

...

12 Commits

Author SHA1 Message Date
github-actions[bot]
fbef61d076 [Backport release-1.2] Fix: disable cochange for apprev when def changes (#3221)
* Fix: disable cochange for apprev when def changes

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit f67d1c7e08)

* Fix: add test

Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit e3f95763ae)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-02-10 11:42:04 +08:00
github-actions[bot]
52f9b7e691 [Backport release-1.2] Fix: upgrade package github.com/docker/cli for CVE-2021-41092 (#3218)
* Fix: upgrade package github.com/docker/cli for CVE-2021-41092

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit c0ce5b9931)

* Chore: change go version to 1.17

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 976d781dc2)

* Chore: change go mod

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 01724c9bf5)

* Fix: change install cue shell

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 3bab8d57fa)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-02-09 19:21:03 +08:00
github-actions[bot]
e721449c46 [Backport release-1.2] Feat: the golang version upgrade to 1.7 (#3213)
* Feat: the golang version upgrade to 1.7

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit f7e75a18a0)

* Feat: install expat lib

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit e641c39509)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-02-09 15:25:59 +08:00
github-actions[bot]
ab998ce3f4 Fix: fix flag conflict (#3212)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 440c9947c7)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-02-09 15:22:33 +08:00
github-actions[bot]
bcc978380f [Backport release-1.2] Feat: support install and uninstall vela core (#3209)
* Feat: support install and uninstall vela core

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 330b20cabd)

* Feat: support upgrade crd

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 61505a92d3)

* Feat: support set reuse args

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 9ae203f961)

* Feat: apply CRD before install or upgrade

Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 0815ba6580)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-02-09 11:58:11 +08:00
github-actions[bot]
b9f9f7f3f9 Fix: support more Terraform variable types (#3207)
Support Any, set and some complicated variable types

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit c37ba34fc7)

Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-02-08 18:50:58 +08:00
github-actions[bot]
18ceb467ed Feat: add componentName in context for traitDefinition (#3202)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 2e07453888)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-02-08 16:27:19 +08:00
github-actions[bot]
06eb8f055d [Backport release-1.2] Feat: dispatch manifests in concurrent (#3201)
* Feat: dispatch manifests in concurrent

Signed-off-by: yangsoon <songyang.song@alibaba-inc.com>
(cherry picked from commit 774f108d19)

* Fix: merge workflow pkg convert to pkg util

Signed-off-by: yangsoon <songyang.song@alibaba-inc.com>
(cherry picked from commit 0a09a2fa8d)

Co-authored-by: yangsoon <songyang.song@alibaba-inc.com>
2022-02-08 16:26:54 +08:00
github-actions[bot]
9dec98fbba Fix: add unit test for getting Terraform Configuration from remote git (#3194)
Add another unit test when the configuration of Terraform locates
in a subpath of a git remote repository

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
(cherry picked from commit bef07e9a08)

Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-02-08 10:21:41 +08:00
github-actions[bot]
42e7f04267 Fix: error msg in webhook too complicated to read (#3170)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit ddc272efa1)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-01-28 16:19:59 +08:00
github-actions[bot]
188e453f8a Fix: Use systemDefinitionNamespace core Helm Chart (#3165)
Use value in Addon Registry CM and the Test Application

Signed-off-by: Oliver Otte <otte@gonicus.de>
(cherry picked from commit 8498c52aee)

Co-authored-by: Oliver Otte <otte@gonicus.de>
2022-01-25 19:17:56 +08:00
github-actions[bot]
c34cd657e8 Fix: krew install template of kubectl-vela.exe (#3164)
Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 533f7820a6)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-01-25 18:51:59 +08:00
76 changed files with 2085 additions and 270 deletions

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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'

View File

@@ -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

View File

@@ -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 }}

View File

@@ -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'

View File

@@ -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"

View File

@@ -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 /

View File

@@ -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 /

View File

@@ -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 /

View File

@@ -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)

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View File

@@ -1,3 +1,4 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*

View File

@@ -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": ""
}
}
}'
}'

View File

@@ -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

View File

@@ -117,7 +117,7 @@ multicluster:
image:
repository: oamdev/cluster-gateway
tag: v1.1.7
pullPolicy: Always
pullPolicy: IfNotPresent
resources:
limits:
cpu: 100m

View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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()
},
})

View File

@@ -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
`
)

View File

@@ -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))
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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"

View File

@@ -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

View File

@@ -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
}

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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++ {

View File

@@ -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"
...

View File

@@ -1,8 +0,0 @@
#String: {
#do: "string"
#provider: "convert"
bt: bytes
str?: string
...
}

View File

@@ -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
View File

@@ -0,0 +1,17 @@
#PatchK8sObject: {
#do: "patch-k8s-object"
#provider: "util"
value: {...}
patch: {...}
result: {...}
...
}
#String: {
#do: "string"
#provider: "util"
bt: bytes
str?: string
...
}

View 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
}

View 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())
})
})

View 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")
}

Binary file not shown.

24
pkg/utils/helm/testdata/index.yaml vendored Normal file
View 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"

View File

@@ -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
}

View File

@@ -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

View File

@@ -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:

View File

@@ -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,
})
}

View File

@@ -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)
}

View File

@@ -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,
})
}

View File

@@ -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) {

View File

@@ -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"

View 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,
})
}

View 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)
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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())
}
})

View File

@@ -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
View 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
}

View 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())
})
})

View File

@@ -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
View 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)
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}