Compare commits

...

25 Commits

Author SHA1 Message Date
github-actions[bot]
45e1de19dc Fix: log message wraps wrong arguments (#3793)
Signed-off-by: StevenLeiZhang <zhangleiic@163.com>
(cherry picked from commit 79362f0648)

Co-authored-by: StevenLeiZhang <zhangleiic@163.com>
2022-04-29 13:34:15 +08:00
github-actions[bot]
d910bb7928 [Backport release-1.3] Chore: sync the cli binaries to OSS (#3785)
* Feat: show the parsing capability error message

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

* Chore: sync the cli binaries to OSS

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

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-04-29 10:14:43 +08:00
github-actions[bot]
a580c9a44c Fix: env trait compatible with valueFrom (#3784)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit d0506db414)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-04-28 18:19:01 +08:00
github-actions[bot]
8f5eaefd89 Fix: kubectl check err (#3779)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 3bbcbe0e6f)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-04-28 16:33:28 +08:00
github-actions[bot]
7c3a35ae87 [Backport release-1.3] Fix: addon cli parse any type (#3777)
* fix addon parse any type

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit fdda3a70e5)

* test int

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit ca47004529)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-28 16:08:48 +08:00
github-actions[bot]
ea0003f7cb Fix: fix revision in webservice (#3766)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit 1ab13437b4)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-04-27 14:19:43 +08:00
Zheng Xi Zhou
3728857c82 Fix: use Terraform provider name as application in CLI (#3742) (#3756)
* Fix: use Terraform provider name as application in CLI

In CLI, use Terraform provider name as application name when
create a Provider. Also display there providers in VelaUX.
1). manually created a Terraform Provider object, like https://github.com/oam-dev/terraform-controller/blob/master/getting-started.md#aws
2). by enabling a Terraform provider addon in version older than v1.3.0
3). by create a Terraform provider via `vela provider add`
4). by VelaUX

Signed-off-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-04-26 22:17:17 +08:00
github-actions[bot]
8e6c49cb37 [Backport release-1.3] Fix: fix the bug of vela cli enable addon by localDir on windows os (#3762)
* fix windows bug

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix several issue

fix bug

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix unit-test

(cherry picked from commit 956dff3261)

* add more tests

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 497a6ebcae)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-26 20:53:34 +08:00
github-actions[bot]
4b31274bda [Backport release-1.3] Fix: velaux addon hint after enable (#3760)
* Fix: velaux addon hint after enable

Signed-off-by: qiaozp <chivalry.pp@gmail.com>
(cherry picked from commit 5adfd6210f)

* check if upgrade

Signed-off-by: qiaozp <chivalry.pp@gmail.com>
(cherry picked from commit 5a7467a494)

Co-authored-by: qiaozp <chivalry.pp@gmail.com>
2022-04-26 16:47:36 +08:00
github-actions[bot]
429e62d11b [Backport release-1.3] Feat: check whether a project matched a config's project (#3757)
* Feat: check whether a project matched a config's project

If the config project is not nil, it's matched whether the project
matched the target project.
If the config project is nil, the target project matched the config.

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

Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-04-26 14:59:41 +08:00
github-actions[bot]
841a18189a Fix: public image registry config could not be created (#3748)
Fix #3663

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

Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-04-25 13:59:00 +08:00
github-actions[bot]
59bd066c05 use unical project filter func to list secret (#3746)
Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix pointer

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit b441878545)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-25 10:06:37 +08:00
github-actions[bot]
efa9dedb85 Fix: vela-cli does not print cluster name, if application installed in default cluster (#3740)
Signed-off-by: StevenLeiZhang <zhangleiic@163.com>
(cherry picked from commit 3819981dd4)

Co-authored-by: StevenLeiZhang <zhangleiic@163.com>
2022-04-24 09:17:39 +08:00
Xiangbo Ma
fdffde4dfd Fix: cherry-pick #3724 to delete apprev annotation. Signed-off-by: Xiangbo Ma <maxiangboo@cmbchina.com> (#3739)
Signed-off-by: fourierr <maxiangboo@qq.com>
2022-04-24 09:17:15 +08:00
wyike
d08aa7d12c fix several issues (#3729) (#3735)
Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-22 17:29:01 +08:00
wyike
f9755a405f Fix: change systemInfo some fields (cp #3715) (#3723)
* Fix: change systemInfo some  fields (#3715)

* add some field an calculate workflow step

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

* fix the calculate job cannot start issue

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

* fix comments

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix test

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

* add suit test framework

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

* modify the go mod file

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix worry file name

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-22 16:42:54 +08:00
github-actions[bot]
d751d95bac Feat: change the webservice and config-image-registry definitions (#3733)
Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
(cherry picked from commit 300f0c5ace)

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-04-22 16:34:28 +08:00
github-actions[bot]
e86eec07e0 specify staticcheck version (#3728)
Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix the workflow

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix

try to fix

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix make file

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>

fix makefile

Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
(cherry picked from commit 7b62664332)

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-22 14:26:37 +08:00
github-actions[bot]
4abb5c6ced [Backport release-1.3] Fix: embed.FS filepath that follow the unix style file path when running on windows (#3720)
* fix: "builtin-apply-component.cue: file does not exist"

Signed-off-by: lei.chu <1062186165@qq.com>
(cherry picked from commit fba60a1af1)

* fix: "builtin-apply-component.cue: file does not exist"

Signed-off-by: lei.chu <1062186165@qq.com>
(cherry picked from commit 9e74023951)

Co-authored-by: lei.chu <1062186165@qq.com>
2022-04-21 14:32:30 +08:00
github-actions[bot]
32d9a9ec94 Fix: vela-core does not report error, when component depends on invalid component (#3712)
Signed-off-by: StevenLeiZhang <zhangleiic@163.com>
(cherry picked from commit 01781bdc02)

Co-authored-by: StevenLeiZhang <zhangleiic@163.com>
2022-04-20 13:39:53 +08:00
github-actions[bot]
f6f9ef4ded Feat: support disable legacy gc upgrade operation (#3697)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 31ab3d859c)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-04-19 09:53:10 +08:00
github-actions[bot]
166c93d548 Fix: set provider name as the config name (#3695)
- For VelaUX, hidden a provider name (users don't need to manual set it). Used
the application/component name (config name) to be the provider name.
- Store description of a config to the annotation of the config application

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

Co-authored-by: Zheng Xi Zhou <zzxwill@gmail.com>
2022-04-18 16:48:37 +08:00
github-actions[bot]
8f767068bf Fix: rt resource key compare mismatch local cluster (#3685)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit fa12bc1950)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-04-15 16:37:00 +08:00
github-actions[bot]
8d9e2a71e7 [Backport release-1.3] Fix: can not query the instance list for the app with apply once policy (#3684)
* Fix: can not query the instance list for the app with apply once policy

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

* Fix: change the test case about ListResourcesInApp

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

Co-authored-by: barnettZQG <barnett.zqg@gmail.com>
2022-04-15 15:04:04 +08:00
wyike
58b3bca537 cherrypick 3665 and 3605 to release 1.3 (#3668)
Signed-off-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-04-15 12:11:05 +08:00
67 changed files with 2051 additions and 416 deletions

View File

@@ -57,7 +57,7 @@ jobs:
restore-keys: ${{ runner.os }}-pkg-
- name: Install StaticCheck
run: GO111MODULE=off go get honnef.co/go/tools/cmd/staticcheck
run: GO111MODULE=on go get honnef.co/go/tools/cmd/staticcheck@v0.3.0
- name: Static Check
run: staticcheck ./...

View File

@@ -8,6 +8,10 @@ on:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUCKET: ${{ secrets.CLI_OSS_BUCKET }}
ENDPOINT: ${{ secrets.CLI_OSS_ENDPOINT }}
ACCESS_KEY: ${{ secrets.CLI_OSS_ACCESS_KEY }}
ACCESS_KEY_SECRET: ${{ secrets.CLI_OSS_ACCESS_KEY_SECRET }}
jobs:
build:
@@ -104,6 +108,18 @@ jobs:
name: sha256sums
path: ./_bin/sha256-${{ steps.get_matrix.outputs.OS }}-${{ steps.get_matrix.outputs.ARCH }}.txt
retention-days: 1
- name: Install ossutil
run: wget http://gosspublic.alicdn.com/ossutil/1.7.0/ossutil64 && chmod +x ossutil64 && mv ossutil64 ossutil
- name: Configure Alibaba Cloud OSSUTIL
run: ./ossutil --config-file .ossutilconfig config -i ${ACCESS_KEY} -k ${ACCESS_KEY_SECRET} -e ${ENDPOINT} -c .ossutilconfig
- name: sync local to cloud
run: ./ossutil --config-file .ossutilconfig sync ./_bin/vela oss://$BUCKET/binary/vela/${{ env.VELA_VERSION }}
- name: sync the latest version file
run: |
touch ${{ env.VELA_VERSION }} > ./latest_version
./ossutil --config-file .ossutilconfig ./latest_version oss://$BUCKET/binary/vela/latest_version
upload-plugin-homebrew:
needs: build

View File

@@ -31,6 +31,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/interfaces"
velatypes "github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/utils/errors"
)
@@ -121,7 +122,11 @@ func (in ManagedResource) NamespacedName() types.NamespacedName {
// ResourceKey computes the key for managed resource, resources with the same key points to the same resource
func (in ManagedResource) ResourceKey() string {
gv, kind := in.GroupVersionKind().ToAPIVersionAndKind()
return strings.Join([]string{gv, kind, in.Cluster, in.Namespace, in.Name}, "/")
cluster := in.Cluster
if cluster == "" {
cluster = velatypes.ClusterLocalName
}
return strings.Join([]string{gv, kind, cluster, in.Namespace, in.Name}, "/")
}
// ComponentKey computes the key for the component which managed resource belongs to

View File

@@ -22,6 +22,9 @@ import (
)
const (
// ClusterLocalName the name for the hub cluster
ClusterLocalName = "local"
// CredentialTypeInternal identifies the virtual cluster from internal kubevela system
CredentialTypeInternal v1alpha1.CredentialType = "Internal"
// CredentialTypeOCMManagedCluster identifies the virtual cluster from ocm

View File

@@ -73,8 +73,8 @@ const (
LabelConfigSyncToMultiCluster = "config.oam.dev/multi-cluster"
// LabelConfigIdentifier is the label for config identifier
LabelConfigIdentifier = "config.oam.dev/identifier"
// LabelConfigDescription is the label for config description
LabelConfigDescription = "config.oam.dev/description"
// AnnotationConfigDescription is the annotation for config description
AnnotationConfigDescription = "config.oam.dev/description"
// AnnotationConfigAlias is the annotation for config alias
AnnotationConfigAlias = "config.oam.dev/alias"
)
@@ -148,3 +148,15 @@ const (
// HelmRepository is the config type for Helm chart repository
HelmRepository = "config-helm-repository"
)
const (
// TerraformComponentPrefix is the prefix of component type of terraform-xxx
TerraformComponentPrefix = "terraform-"
// ProviderAppPrefix is the prefix of the application to create a Terraform Provider
ProviderAppPrefix = "config-terraform-provider"
// ProviderNamespace is the namespace of Terraform Cloud Provider
ProviderNamespace = "default"
// VelaCoreConfig is to mark application, config and its secret or Terraform provider lelong to a KubeVela config
VelaCoreConfig = "velacore-config"
)

View File

@@ -10,6 +10,7 @@ metadata:
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
custom.definition.oam.dev/multi-cluster.config.oam.dev: "true"
custom.definition.oam.dev/type.config.oam.dev: image-registry
custom.definition.oam.dev/ui-hidden: "true"
name: config-image-registry
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
@@ -35,20 +36,23 @@ spec:
"config.oam.dev/sub-type": "auth"
}
}
type: "kubernetes.io/dockerconfigjson"
stringData: {
if parameter.auth != _|_ {
".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
if parameter.auth != _|_ {
type: "kubernetes.io/dockerconfigjson"
}
if parameter.auth == _|_ {
type: "Opaque"
}
if parameter.auth != _|_ {
stringData: ".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
}
})
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
})
}
}
parameter: {

View File

@@ -46,7 +46,7 @@ spec:
}]
}
if _baseEnv != _|_ {
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar.value}}
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar}}
// +patchStrategy=replace
env: [ for envVar in _baseEnv if _delKeys[envVar.name] == _|_ && !_params.replace {
name: envVar.name
@@ -54,11 +54,15 @@ spec:
value: _params.env[envVar.name]
}
if _params.env[envVar.name] == _|_ {
value: envVar.value
if envVar.value != _|_ {
value: envVar.value
}
if envVar.valueFrom != _|_ {
valueFrom: envVar.valueFrom
}
}
}] + [ for k, v in _params.env if _delKeys[k] == _|_ && (_params.replace || _baseEnvMap[k] == _|_) {
name: k
value: v
v
}]
}
}

View File

@@ -132,10 +132,9 @@ spec:
parameter.labels
}
if parameter.addRevisionLabel {
"app.oam.dev/appRevision": context.appRevision
"app.oam.dev/revision": context.revision
}
"app.oam.dev/component": context.name
"app.oam.dev/revision": context.revision
}
if parameter.annotations != _|_ {
annotations: parameter.annotations
@@ -333,7 +332,7 @@ spec:
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
// +ignore
// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
// +usage=If addRevisionLabel is true, the revision label will be added to the underlying pods
addRevisionLabel: *false | bool
// +usage=Commands to run in the container
@@ -455,7 +454,7 @@ spec:
readinessProbe?: #HealthProbe
// +usage=Specify the hostAliases to add
hostAliases: [...{
hostAliases?: [...{
ip: string
hostnames: [...string]
}]

View File

@@ -10,6 +10,7 @@ metadata:
custom.definition.oam.dev/catalog.config.oam.dev: velacore-config
custom.definition.oam.dev/multi-cluster.config.oam.dev: "true"
custom.definition.oam.dev/type.config.oam.dev: image-registry
custom.definition.oam.dev/ui-hidden: "true"
name: config-image-registry
namespace: {{ include "systemDefinitionNamespace" . }}
spec:
@@ -35,20 +36,23 @@ spec:
"config.oam.dev/sub-type": "auth"
}
}
type: "kubernetes.io/dockerconfigjson"
stringData: {
if parameter.auth != _|_ {
".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
if parameter.auth != _|_ {
type: "kubernetes.io/dockerconfigjson"
}
if parameter.auth == _|_ {
type: "Opaque"
}
if parameter.auth != _|_ {
stringData: ".dockerconfigjson": json.Marshal({
auths: "\(parameter.registry)": {
username: parameter.auth.username
password: parameter.auth.password
if parameter.auth.email != _|_ {
email: parameter.auth.email
}
})
}
auth: base64.Encode(null, (parameter.auth.username + ":" + parameter.auth.password))
}
})
}
}
parameter: {

View File

@@ -46,7 +46,7 @@ spec:
}]
}
if _baseEnv != _|_ {
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar.value}}
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar}}
// +patchStrategy=replace
env: [ for envVar in _baseEnv if _delKeys[envVar.name] == _|_ && !_params.replace {
name: envVar.name
@@ -54,11 +54,15 @@ spec:
value: _params.env[envVar.name]
}
if _params.env[envVar.name] == _|_ {
value: envVar.value
if envVar.value != _|_ {
value: envVar.value
}
if envVar.valueFrom != _|_ {
valueFrom: envVar.valueFrom
}
}
}] + [ for k, v in _params.env if _delKeys[k] == _|_ && (_params.replace || _baseEnvMap[k] == _|_) {
name: k
value: v
v
}]
}
}

View File

@@ -132,10 +132,9 @@ spec:
parameter.labels
}
if parameter.addRevisionLabel {
"app.oam.dev/appRevision": context.appRevision
"app.oam.dev/revision": context.revision
}
"app.oam.dev/component": context.name
"app.oam.dev/revision": context.revision
}
if parameter.annotations != _|_ {
annotations: parameter.annotations
@@ -333,7 +332,7 @@ spec:
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
// +ignore
// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
// +usage=If addRevisionLabel is true, the revision label will be added to the underlying pods
addRevisionLabel: *false | bool
// +usage=Commands to run in the container
@@ -455,7 +454,7 @@ spec:
readinessProbe?: #HealthProbe
// +usage=Specify the hostAliases to add
hostAliases: [...{
hostAliases?: [...{
ip: string
hostnames: [...string]
}]

View File

@@ -0,0 +1,23 @@
name: mock-addon
version: 1.0.0
description: Extended workload to do continuous and progressive delivery
icon: https://raw.githubusercontent.com/fluxcd/flux/master/docs/_files/weave-flux.png
url: https://fluxcd.io
tags:
- extended_workload
- gitops
- only_example
deployTo:
control_plane: true
runtime_cluster: false
dependencies: []
#- name: addon_name
# set invisible means this won't be list and will be enabled when depended on
# for example, terraform-alibaba depends on terraform which is invisible,
# when terraform-alibaba is enabled, terraform will be enabled automatically
# default: false
invisible: false

View File

@@ -0,0 +1,14 @@
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: mock-addon
namespace: vela-system
spec:
components:
- name: ns-example-system
type: raw
properties:
apiVersion: v1
kind: Namespace
metadata:
name: mock-system

View File

@@ -129,7 +129,7 @@ var _ = Describe("Test Kubectl Plugin", func() {
Expect(err).NotTo(HaveOccurred())
Expect(output).Should(ContainSubstring(showTdResult))
})
PIt("Test show componentDefinition use Helm Charts as Workload", func() {
It("Test show componentDefinition use Helm Charts as Workload", func() {
Eventually(func() string {
cdName := "test-webapp-chart"
output, _ := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", cdName))

14
go.mod
View File

@@ -63,11 +63,11 @@ require (
github.com/wonderflow/cert-manager-api v1.0.3
go.mongodb.org/mongo-driver v1.5.1
go.uber.org/zap v1.18.1
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/tools v0.1.6 // indirect
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/src-d/go-git.v4 v4.13.1
@@ -97,6 +97,8 @@ require (
sigs.k8s.io/yaml v1.2.0
)
require github.com/robfig/cron/v3 v3.0.1
require (
cloud.google.com/go v0.81.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
@@ -106,7 +108,7 @@ require (
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/BurntSushi/toml v0.4.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
@@ -249,8 +251,8 @@ require (
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/mod v0.4.2 // indirect
golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect

27
go.sum
View File

@@ -110,8 +110,9 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
@@ -1437,6 +1438,8 @@ github.com/rancher/wrangler v0.4.0/go.mod h1:1cR91WLhZgkZ+U4fV9nVuXqKurWbgXcIReU
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -1654,7 +1657,7 @@ github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE=
@@ -1796,8 +1799,9 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1841,8 +1845,9 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1907,9 +1912,10 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1922,8 +1928,9 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602 h1:0Ja1LBD+yisY6RWM/BH7TJVXWsSjs2VwBSmvSX4HdBc=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -2046,7 +2053,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
@@ -2202,8 +2209,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6 h1:SIasE1FVIQOWz2GEAHFOmoW7xchJcqlucjSULTL0Ag4=
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a h1:ofrrl6c6NG5/IOSx/R1cyiQxxjqlur0h/TvbUhkH0II=
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -25,7 +25,7 @@ ifeq (, $(shell which staticcheck))
@{ \
set -e ;\
echo 'installing honnef.co/go/tools/cmd/staticcheck ' ;\
GO111MODULE=off go get honnef.co/go/tools/cmd/staticcheck ;\
GO111MODULE=on go get honnef.co/go/tools/cmd/staticcheck@v0.3.0 ;\
}
STATICCHECK=$(GOBIN)/staticcheck
else

View File

@@ -49,6 +49,7 @@ e2e-apiserver-test:
pkill vela_addon_mock_server || true
go run ./e2e/addon/mock/vela_addon_mock_server.go &
go test -v -coverpkg=./... -coverprofile=/tmp/e2e_apiserver_test.out ./test/e2e-apiserver-test
sleep 15
@$(OK) tests pass
.PHONY: e2e-test

View File

@@ -195,6 +195,10 @@ func GetPatternFromItem(it Item, r AsyncReader, rootPath string) string {
if strings.HasPrefix(relativePath, strings.Join([]string{rootPath, p.Value}, "/")) {
return p.Value
}
if strings.HasPrefix(relativePath, filepath.Join(rootPath, p.Value)) {
// for enable addon by load dir, compatible with linux or windows os
return p.Value
}
}
return ""
}
@@ -357,7 +361,7 @@ func readResFile(a *InstallPackage, reader AsyncReader, readPath string) error {
if filename == "parameter.cue" {
return nil
}
file := ElementFile{Data: b, Name: path.Base(readPath)}
file := ElementFile{Data: b, Name: filepath.Base(readPath)}
switch filepath.Ext(filename) {
case ".cue":
a.CUETemplates = append(a.CUETemplates, file)
@@ -375,7 +379,7 @@ func readDefSchemaFile(a *InstallPackage, reader AsyncReader, readPath string) e
if err != nil {
return err
}
a.DefSchemas = append(a.DefSchemas, ElementFile{Data: b, Name: path.Base(readPath)})
a.DefSchemas = append(a.DefSchemas, ElementFile{Data: b, Name: filepath.Base(readPath)})
return nil
}
@@ -386,7 +390,7 @@ func readDefFile(a *UIData, reader AsyncReader, readPath string) error {
return err
}
filename := path.Base(readPath)
file := ElementFile{Data: b, Name: path.Base(readPath)}
file := ElementFile{Data: b, Name: filepath.Base(readPath)}
switch filepath.Ext(filename) {
case ".cue":
a.CUEDefinitions = append(a.CUEDefinitions, file)

View File

@@ -21,6 +21,8 @@ import (
"fmt"
"time"
"github.com/oam-dev/kubevela/pkg/utils/apply"
"github.com/oam-dev/cluster-gateway/pkg/apis/cluster/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/runtime"
@@ -362,6 +364,21 @@ var _ = Describe("func addon update ", func() {
})
})
var _ = Describe("test enable addon in local dir", func() {
BeforeEach(func() {
app := v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-example"}}
Expect(k8sClient.Delete(ctx, &app)).Should(SatisfyAny(BeNil(), util.NotFoundMatcher{}))
})
It("test enable addon by local dir", func() {
ctx := context.Background()
err := EnableAddonByLocalDir(ctx, "example", "./testdata/example", k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, map[string]interface{}{"example": "test"})
Expect(err).Should(BeNil())
app := v1beta1.Application{}
Expect(k8sClient.Get(ctx, types2.NamespacedName{Namespace: "vela-system", Name: "addon-example"}, &app)).Should(BeNil())
})
})
const (
appYaml = `apiVersion: core.oam.dev/v1beta1
kind: Application

View File

@@ -282,7 +282,7 @@ func (u *Cache) discoverAndRefreshRegistry() {
for _, addon := range uiDatas {
uiData, err := versionedRegistry.GetAddonUIData(context.Background(), addon.Name, addon.Version)
if err != nil {
log.Logger.Errorf("fail to get addon from registry %s, addon %s version %s for cache updating, %v", addon.Name, r.Name, err)
log.Logger.Errorf("fail to get addon from registry %s, addon %s version %s for cache updating, %v", r.Name, addon.Name, addon.Version, err)
continue
}
u.putVersionedUIData2Cache(r.Name, addon.Name, addon.Version, uiData)

View File

@@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"path/filepath"
"k8s.io/client-go/discovery"
"k8s.io/klog/v2"
@@ -92,7 +93,11 @@ func DisableAddon(ctx context.Context, cli client.Client, name string, config *r
// EnableAddonByLocalDir enable an addon from local dir
func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli client.Client, dc *discovery.DiscoveryClient, applicator apply.Applicator, config *rest.Config, args map[string]interface{}) error {
r := localReader{dir: dir, name: name}
absDir, err := filepath.Abs(dir)
if err != nil {
return err
}
r := localReader{dir: absDir, name: name}
metas, err := r.ListAddonMeta()
if err != nil {
return err

View File

@@ -37,8 +37,10 @@ func (l localReader) ListAddonMeta() (map[string]SourceMeta, error) {
}
func (l localReader) ReadFile(path string) (string, error) {
file := strings.TrimPrefix(path, l.name+"/")
b, err := ioutil.ReadFile(filepath.Clean(filepath.Join(l.dir, file)))
path = strings.TrimPrefix(path, l.name+"/")
// for windows
path = strings.TrimPrefix(path, l.name+"\\")
b, err := ioutil.ReadFile(filepath.Clean(filepath.Join(l.dir, path)))
if err != nil {
return "", err
}
@@ -46,7 +48,8 @@ func (l localReader) ReadFile(path string) (string, error) {
}
func (l localReader) RelativePath(item Item) string {
return filepath.Join(l.name, strings.TrimPrefix(item.GetPath()+"/", l.dir))
file := strings.TrimPrefix(item.GetPath(), filepath.Clean(l.dir))
return filepath.Join(l.name, file)
}
func recursiveFetchFiles(path string, metas *SourceMeta) error {
@@ -60,7 +63,7 @@ func recursiveFetchFiles(path string, metas *SourceMeta) error {
return err
}
} else {
metas.Items = append(metas.Items, OSSItem{tp: "file", path: fmt.Sprintf("%s/%s", path, file.Name()), name: file.Name()})
metas.Items = append(metas.Items, OSSItem{tp: "file", path: filepath.Join(path, file.Name()), name: file.Name()})
}
}
return nil

View File

@@ -0,0 +1,100 @@
/*
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 collect
import (
"context"
"fmt"
"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/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
"github.com/oam-dev/kubevela/pkg/apiserver/datastore/kubeapi"
"github.com/oam-dev/kubevela/pkg/apiserver/datastore/mongodb"
"github.com/oam-dev/kubevela/pkg/utils/common"
)
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
func TestCalculateJob(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Caclculate systemInfo cronJob")
}
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),
CRDDirectoryPaths: []string{"../../../charts/vela-core/crds"},
}
By("start kube test env")
var err error
cfg, err = testEnv.Start()
Expect(err).Should(BeNil())
Expect(cfg).ToNot(BeNil())
By("new kube client")
cfg.Timeout = time.Minute * 2
k8sClient, err = client.New(cfg, client.Options{Scheme: common.Scheme})
Expect(err).Should(BeNil())
Expect(k8sClient).ToNot(BeNil())
By("new kube client success")
clients.SetKubeClient(k8sClient)
Expect(err).Should(BeNil())
close(done)
}, 240)
var _ = AfterSuite(func() {
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).ToNot(HaveOccurred())
})
func NewDatastore(cfg datastore.Config) (ds datastore.DataStore, err error) {
switch cfg.Type {
case "mongodb":
ds, err = mongodb.New(context.Background(), cfg)
if err != nil {
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
}
case "kubeapi":
ds, err = kubeapi.New(context.Background(), cfg)
if err != nil {
return nil, fmt.Errorf("create mongodb datastore instance failure %w", err)
}
default:
return nil, fmt.Errorf("not support datastore type %s", cfg.Type)
}
return ds, nil
}

View File

@@ -0,0 +1,331 @@
/*
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 collect
import (
"context"
"sort"
"time"
client2 "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
"github.com/oam-dev/kubevela/pkg/multicluster"
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
"github.com/oam-dev/kubevela/pkg/apiserver/log"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
"github.com/robfig/cron/v3"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
)
// TopKFrequent top frequency component or trait definition
var TopKFrequent = 5
// CrontabSpec the cron spec of job running
var CrontabSpec = "0 0 * * *"
// maximum tires is 5, initial duration is 1 minute
var waitBackOff = wait.Backoff{
Steps: 5,
Duration: 1 * time.Minute,
Factor: 5.0,
Jitter: 0.1,
}
// InfoCalculateCronJob is the cronJob to calculate the system info store in db
type InfoCalculateCronJob struct {
ds datastore.DataStore
}
// StartCalculatingInfoCronJob will start the system info calculating job.
func StartCalculatingInfoCronJob(ds datastore.DataStore) {
i := InfoCalculateCronJob{
ds: ds,
}
// run calculate job in 0:00 of every day
i.start(CrontabSpec)
}
func (i InfoCalculateCronJob) start(cronSpec string) {
c := cron.New(cron.WithChain(
// don't let job panic crash whole api-server process
cron.Recover(cron.DefaultLogger),
))
// ignore the entityId and error, the cron spec is defined by hard code, mustn't generate error
_, _ = c.AddFunc(cronSpec, func() {
// ExponentialBackoff retry this job
err := retry.OnError(waitBackOff, func(err error) bool {
// always retry
return true
}, func() error {
if err := i.run(); err != nil {
log.Logger.Errorf("Failed to calculate systemInfo, will try again after several minute error %v", err)
return err
}
log.Logger.Info("Successfully to calculate systemInfo")
return nil
})
if err != nil {
log.Logger.Errorf("After 5 tries the calculating cronJob failed: %v", err)
}
})
c.Start()
}
func (i InfoCalculateCronJob) run() error {
ctx := context.Background()
systemInfo := model.SystemInfo{}
e, err := i.ds.List(ctx, &systemInfo, &datastore.ListOptions{})
if err != nil {
return err
}
// if no systemInfo means velaux have not have not send get requestso skip calculate job
if len(e) == 0 {
return nil
}
info, ok := e[0].(*model.SystemInfo)
if !ok {
return nil
}
// if disable collection skip calculate job
if !info.EnableCollection {
return nil
}
if err := i.calculateAndUpdate(ctx, *info); err != nil {
return err
}
return nil
}
func (i InfoCalculateCronJob) calculateAndUpdate(ctx context.Context, systemInfo model.SystemInfo) error {
appCount, topKComp, topKTrait, topWorkflowStep, topKPolicy, err := i.calculateAppInfo(ctx)
if err != nil {
return err
}
enabledAddon, err := i.calculateAddonInfo(ctx)
if err != nil {
return err
}
clusterCount, err := i.calculateClusterInfo(ctx)
if err != nil {
return err
}
statisticInfo := model.StatisticInfo{
AppCount: genCountInfo(appCount),
TopKCompDef: topKComp,
TopKTraitDef: topKTrait,
TopKWorkflowStepDef: topWorkflowStep,
TopKPolicyDef: topKPolicy,
ClusterCount: genClusterCountInfo(clusterCount),
EnabledAddon: enabledAddon,
UpdateTime: time.Now(),
}
systemInfo.StatisticInfo = statisticInfo
if err := i.ds.Put(ctx, &systemInfo); err != nil {
return err
}
return nil
}
func (i InfoCalculateCronJob) calculateAppInfo(ctx context.Context) (int, []string, []string, []string, []string, error) {
var err error
var appCount int
compDef := map[string]int{}
traitDef := map[string]int{}
workflowDef := map[string]int{}
policyDef := map[string]int{}
var app = model.Application{}
entities, err := i.ds.List(ctx, &app, &datastore.ListOptions{})
if err != nil {
return 0, nil, nil, nil, nil, err
}
for _, entity := range entities {
appModel, ok := entity.(*model.Application)
if !ok {
continue
}
appCount++
comp := model.ApplicationComponent{
AppPrimaryKey: appModel.Name,
}
comps, err := i.ds.List(ctx, &comp, &datastore.ListOptions{})
if err != nil {
return 0, nil, nil, nil, nil, err
}
for _, e := range comps {
c, ok := e.(*model.ApplicationComponent)
if !ok {
continue
}
compDef[c.Type]++
for _, t := range c.Traits {
traitDef[t.Type]++
}
}
workflow := model.Workflow{
AppPrimaryKey: app.PrimaryKey(),
}
workflows, err := i.ds.List(ctx, &workflow, &datastore.ListOptions{})
if err != nil {
return 0, nil, nil, nil, nil, err
}
for _, e := range workflows {
w, ok := e.(*model.Workflow)
if !ok {
continue
}
for _, step := range w.Steps {
workflowDef[step.Type]++
}
}
policy := model.ApplicationPolicy{
AppPrimaryKey: app.PrimaryKey(),
}
policies, err := i.ds.List(ctx, &policy, &datastore.ListOptions{})
if err != nil {
return 0, nil, nil, nil, nil, err
}
for _, e := range policies {
p, ok := e.(*model.ApplicationPolicy)
if !ok {
continue
}
policyDef[p.Type]++
}
}
return appCount, topKFrequent(compDef, TopKFrequent), topKFrequent(traitDef, TopKFrequent), topKFrequent(workflowDef, TopKFrequent), topKFrequent(policyDef, TopKFrequent), nil
}
func (i InfoCalculateCronJob) calculateAddonInfo(ctx context.Context) (map[string]string, error) {
client, err := clients.GetKubeClient()
if err != nil {
return nil, err
}
apps := &v1beta1.ApplicationList{}
if err := client.List(ctx, apps, client2.InNamespace(types.DefaultKubeVelaNS), client2.HasLabels{oam.LabelAddonName}); err != nil {
return nil, err
}
res := map[string]string{}
for _, application := range apps.Items {
if addonName := application.Labels[oam.LabelAddonName]; addonName != "" {
var status string
switch application.Status.Phase {
case common.ApplicationRunning:
status = "enabled"
case common.ApplicationDeleting:
status = "disabling"
default:
status = "enabling"
}
res[addonName] = status
}
}
return res, nil
}
func (i InfoCalculateCronJob) calculateClusterInfo(ctx context.Context) (int, error) {
client, err := clients.GetKubeClient()
if err != nil {
return 0, err
}
cs, err := multicluster.ListVirtualClusters(ctx, client)
if err != nil {
return 0, err
}
return len(cs), nil
}
type defPair struct {
name string
count int
}
func topKFrequent(defs map[string]int, k int) []string {
var pairs []defPair
var res []string
for name, num := range defs {
pairs = append(pairs, defPair{name: name, count: num})
}
sort.Slice(pairs, func(i, j int) bool {
return pairs[i].count >= pairs[j].count
})
i := 0
for _, pair := range pairs {
res = append(res, pair.name)
i++
if i == k {
break
}
}
return res
}
func genCountInfo(num int) string {
switch {
case num < 10:
return "<10"
case num < 50:
return "<50"
case num < 100:
return "<100"
case num < 500:
return "<500"
case num < 2000:
return "<2000"
case num < 5000:
return "<5000"
case num < 10000:
return "<10000"
default:
return ">=10000"
}
}
func genClusterCountInfo(num int) string {
switch {
case num < 3:
return "<3"
case num < 10:
return "<10"
case num < 50:
return "<50"
default:
return ">=50"
}
}

View File

@@ -0,0 +1,273 @@
/*
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 collect
import (
"context"
"errors"
"testing"
"time"
"github.com/onsi/gomega/format"
"gotest.tools/assert"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/oam/util"
)
var _ = Describe("Test calculate cronJob", func() {
var (
ds datastore.DataStore
testProject string
i InfoCalculateCronJob
ctx = context.Background()
)
mockDataInDs := func() {
app1 := model.Application{BaseModel: model.BaseModel{CreateTime: time.Now()}, Name: "app1", Project: testProject}
app2 := model.Application{BaseModel: model.BaseModel{CreateTime: time.Now()}, Name: "app2", Project: testProject}
trait1 := model.ApplicationTrait{Type: "rollout"}
trait2 := model.ApplicationTrait{Type: "expose"}
trait3 := model.ApplicationTrait{Type: "rollout"}
trait4 := model.ApplicationTrait{Type: "patch"}
trait5 := model.ApplicationTrait{Type: "patch"}
trait6 := model.ApplicationTrait{Type: "rollout"}
appComp1 := model.ApplicationComponent{AppPrimaryKey: app1.PrimaryKey(), Name: "comp1", Type: "helm", Traits: []model.ApplicationTrait{trait1, trait4}}
appComp2 := model.ApplicationComponent{AppPrimaryKey: app2.PrimaryKey(), Name: "comp2", Type: "webservice", Traits: []model.ApplicationTrait{trait3}}
appComp3 := model.ApplicationComponent{AppPrimaryKey: app2.PrimaryKey(), Name: "comp3", Type: "webservice", Traits: []model.ApplicationTrait{trait2, trait5, trait6}}
Expect(ds.Add(ctx, &app1)).Should(SatisfyAny(BeNil(), DataExistMatcher{}))
Expect(ds.Add(ctx, &app2)).Should(SatisfyAny(BeNil(), DataExistMatcher{}))
Expect(ds.Add(ctx, &appComp1)).Should(SatisfyAny(BeNil(), DataExistMatcher{}))
Expect(ds.Add(ctx, &appComp2)).Should(SatisfyAny(BeNil(), DataExistMatcher{}))
Expect(ds.Add(ctx, &appComp3)).Should(SatisfyAny(BeNil(), DataExistMatcher{}))
Expect(k8sClient.Create(ctx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "vela-system"}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
Expect(k8sClient.Create(ctx, &v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-fluxcd", Labels: map[string]string{oam.LabelAddonName: "fluxcd"}}, Spec: v1beta1.ApplicationSpec{
Components: []common.ApplicationComponent{},
}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
Expect(k8sClient.Create(ctx, &v1beta1.Application{ObjectMeta: metav1.ObjectMeta{Namespace: "vela-system", Name: "addon-rollout", Labels: map[string]string{oam.LabelAddonName: "rollout"}}, Spec: v1beta1.ApplicationSpec{
Components: []common.ApplicationComponent{},
}})).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
}
BeforeEach(func() {
var err error
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "target-test-kubevela"})
Expect(ds).ShouldNot(BeNil())
Expect(err).Should(BeNil())
testProject = "test-cronjob-project"
mockDataInDs()
i = InfoCalculateCronJob{
ds: ds,
}
systemInfo := model.SystemInfo{InstallID: "test-id", EnableCollection: true}
Expect(ds.Add(ctx, &systemInfo)).Should(SatisfyAny(BeNil(), DataExistMatcher{}))
})
It("Test calculate app Info", func() {
appNum, topKCom, topKTrait, _, _, err := i.calculateAppInfo(ctx)
Expect(err).Should(BeNil())
Expect(appNum).Should(BeEquivalentTo(2))
Expect(topKCom).Should(BeEquivalentTo([]string{"webservice", "helm"}))
Expect(topKTrait).Should(BeEquivalentTo([]string{"rollout", "patch", "expose"}))
})
It("Test calculate addon Info", func() {
enabledAddon, err := i.calculateAddonInfo(ctx)
Expect(err).Should(BeNil())
Expect(enabledAddon).Should(BeEquivalentTo(map[string]string{
"fluxcd": "enabling",
"rollout": "enabling",
}))
})
It("Test calculate cluster Info", func() {
clusterNum, err := i.calculateClusterInfo(ctx)
Expect(err).Should(BeNil())
Expect(clusterNum).Should(BeEquivalentTo(1))
})
It("Test calculateAndUpdate func", func() {
systemInfo := model.SystemInfo{}
es, err := ds.List(ctx, &systemInfo, &datastore.ListOptions{})
Expect(err).Should(BeNil())
Expect(len(es)).Should(BeEquivalentTo(1))
info, ok := es[0].(*model.SystemInfo)
Expect(ok).Should(BeTrue())
Expect(info.InstallID).Should(BeEquivalentTo("test-id"))
Expect(i.calculateAndUpdate(ctx, *info)).Should(BeNil())
systemInfo = model.SystemInfo{}
es, err = ds.List(ctx, &systemInfo, &datastore.ListOptions{})
Expect(err).Should(BeNil())
Expect(len(es)).Should(BeEquivalentTo(1))
info, ok = es[0].(*model.SystemInfo)
Expect(ok).Should(BeTrue())
Expect(info.InstallID).Should(BeEquivalentTo("test-id"))
Expect(info.StatisticInfo.AppCount).Should(BeEquivalentTo("<10"))
Expect(info.StatisticInfo.ClusterCount).Should(BeEquivalentTo("<3"))
Expect(info.StatisticInfo.TopKCompDef).Should(BeEquivalentTo([]string{"webservice", "helm"}))
Expect(info.StatisticInfo.TopKTraitDef).Should(BeEquivalentTo([]string{"rollout", "patch", "expose"}))
Expect(info.StatisticInfo.EnabledAddon).Should(BeEquivalentTo(map[string]string{
"fluxcd": "enabling",
"rollout": "enabling",
}))
})
It("Test run func", func() {
app3 := model.Application{BaseModel: model.BaseModel{CreateTime: time.Now()}, Name: "app3", Project: testProject}
Expect(ds.Add(ctx, &app3)).Should(BeNil())
systemInfo := model.SystemInfo{InstallID: "test-id", EnableCollection: false}
Expect(ds.Put(ctx, &systemInfo)).Should(BeNil())
Expect(i.run()).Should(BeNil())
})
})
func TestGenCountInfo(t *testing.T) {
testcases := []struct {
count int
res string
}{
{
count: 3,
res: "<10",
},
{
count: 14,
res: "<50",
},
{
count: 80,
res: "<100",
},
{
count: 350,
res: "<500",
},
{
count: 1800,
res: "<2000",
},
{
count: 4000,
res: "<5000",
},
{
count: 9000,
res: "<10000",
},
{
count: 30000,
res: ">=10000",
},
}
for _, testcase := range testcases {
assert.Equal(t, genCountInfo(testcase.count), testcase.res)
}
}
func TestGenClusterCountInfo(t *testing.T) {
testcases := []struct {
count int
res string
}{
{
count: 2,
res: "<3",
},
{
count: 7,
res: "<10",
},
{
count: 34,
res: "<50",
},
{
count: 100,
res: ">=50",
},
}
for _, testcase := range testcases {
assert.Equal(t, genClusterCountInfo(testcase.count), testcase.res)
}
}
func TestTopKFrequent(t *testing.T) {
testCases := []struct {
def map[string]int
k int
res []string
}{
{
def: map[string]int{
"rollout": 4,
"patch": 3,
"expose": 6,
},
k: 3,
res: []string{"expose", "rollout", "patch"},
},
{
// just return top2
def: map[string]int{
"rollout": 4,
"patch": 3,
"expose": 6,
},
k: 2,
res: []string{"expose", "rollout"},
},
}
for _, testCase := range testCases {
assert.DeepEqual(t, topKFrequent(testCase.def, testCase.k), testCase.res)
}
}
type DataExistMatcher struct{}
// Match matches error.
func (matcher DataExistMatcher) Match(actual interface{}) (success bool, err error) {
if actual == nil {
return false, nil
}
actualError := actual.(error)
return errors.Is(actualError, datastore.ErrRecordExist), nil
}
// FailureMessage builds an error message.
func (matcher DataExistMatcher) FailureMessage(actual interface{}) (message string) {
return format.Message(actual, "to be already exist")
}
// NegatedFailureMessage builds an error message.
func (matcher DataExistMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return format.Message(actual, "not to be already exist")
}

View File

@@ -16,6 +16,8 @@ limitations under the License.
package model
import "time"
func init() {
RegisterModel(&SystemInfo{})
}
@@ -30,10 +32,11 @@ const (
// SystemInfo systemInfo model
type SystemInfo struct {
BaseModel
InstallID string `json:"installID"`
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType"`
DexConfig DexConfig `json:"dexConfig,omitempty"`
InstallID string `json:"installID"`
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType"`
DexConfig DexConfig `json:"dexConfig,omitempty"`
StatisticInfo StatisticInfo `json:"statisticInfo,omitempty"`
}
// DexConfig dex config
@@ -46,6 +49,18 @@ type DexConfig struct {
EnablePasswordDB bool `json:"enablePasswordDB"`
}
// StatisticInfo the system statistic info
type StatisticInfo struct {
ClusterCount string `json:"clusterCount,omitempty"`
AppCount string `json:"appCount,omitempty"`
EnabledAddon map[string]string `json:"enabledAddon,omitempty"`
TopKCompDef []string `json:"topKCompDef,omitempty"`
TopKTraitDef []string `json:"topKTraitDef,omitempty"`
TopKWorkflowStepDef []string `json:"topKWorkflowStepDef,omitempty"`
TopKPolicyDef []string `json:"topKPolicyDef,omitempty"`
UpdateTime time.Time `json:"updateTime,omitempty"`
}
// DexStorage dex storage
type DexStorage struct {
Type string `json:"type"`

View File

@@ -199,6 +199,7 @@ type Config struct {
Name string `json:"name"`
Project string `json:"project"`
Identifier string `json:"identifier"`
Alias string `json:"alias"`
Description string `json:"description"`
CreatedTime *time.Time `json:"createdTime"`
UpdatedTime *time.Time `json:"updatedTime"`
@@ -408,6 +409,7 @@ type CreateApplicationRequest struct {
type CreateConfigRequest struct {
Name string `json:"name" validate:"checkname"`
Alias string `json:"alias"`
Description string `json:"description"`
Project string `json:"project"`
ComponentType string `json:"componentType" validate:"checkname"`
Properties string `json:"properties,omitempty"`
@@ -1114,13 +1116,27 @@ type DetailRevisionResponse struct {
type SystemInfoResponse struct {
SystemInfo
SystemVersion SystemVersion `json:"systemVersion"`
StatisticInfo StatisticInfo `json:"statisticInfo,omitempty"`
}
// SystemInfo system info
type SystemInfo struct {
InstallID string `json:"installID"`
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType"`
PlatformID string `json:"platformID"`
EnableCollection bool `json:"enableCollection"`
LoginType string `json:"loginType"`
InstallTime time.Time `json:"installTime,omitempty"`
}
// StatisticInfo generated by cronJob running in backend
type StatisticInfo struct {
ClusterCount string `json:"clusterCount,omitempty"`
AppCount string `json:"appCount,omitempty"`
EnableAddonList map[string]string `json:"enableAddonList,omitempty"`
ComponentDefinitionTopList []string `json:"componentDefinitionTopList,omitempty"`
TraitDefinitionTopList []string `json:"traitDefinitionTopList,omitempty"`
WorkflowDefinitionTopList []string `json:"workflowDefinitionTopList,omitempty"`
PolicyDefinitionTopList []string `json:"policyDefinitionTopList,omitempty"`
UpdateTime time.Time `json:"updateTime,omitempty"`
}
// SystemInfoRequest request by update SystemInfo

View File

@@ -23,6 +23,8 @@ import (
"os"
"time"
"github.com/oam-dev/kubevela/pkg/apiserver/collect"
restfulspec "github.com/emicklei/go-restful-openapi/v2"
"github.com/emicklei/go-restful/v3"
"github.com/go-openapi/spec"
@@ -60,6 +62,9 @@ type Config struct {
// AddonCacheTime is how long between two cache operations
AddonCacheTime time.Duration
// DisableStatisticCronJob close the calculate system info cronJob
DisableStatisticCronJob bool
}
type leaderConfig struct {
@@ -141,6 +146,10 @@ func (s *restServer) setupLeaderElection() (*leaderelection.LeaderElectionConfig
Callbacks: leaderelection.LeaderCallbacks{
OnStartedLeading: func(ctx context.Context) {
go velasync.Start(ctx, s.dataStore, restCfg, s.usecases)
if !s.cfg.DisableStatisticCronJob {
collect.StartCalculatingInfoCronJob(s.dataStore)
}
// this process would block the whole process, any other handler should start before this func
s.runWorkflowRecordSync(ctx, s.cfg.LeaderConfig.Duration)
},
OnStoppedLeading: func() {

View File

@@ -20,8 +20,10 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
set "github.com/deckarep/golang-set"
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
@@ -37,13 +39,13 @@ import (
"github.com/oam-dev/kubevela/pkg/apiserver/model"
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
"github.com/oam-dev/kubevela/pkg/definition"
"github.com/oam-dev/kubevela/pkg/utils/config"
)
const (
definitionAlias = definition.UserPrefix + "alias.config.oam.dev"
definitionType = definition.UserPrefix + "type.config.oam.dev"
velaCoreConfig = "velacore-config"
configIsReady = "Ready"
configIsNotReady = "Not ready"
terraformProviderAlias = "Terraform Cloud Provider"
@@ -81,7 +83,7 @@ type configUseCaseImpl struct {
func (u *configUseCaseImpl) ListConfigTypes(ctx context.Context, query string) ([]*apis.ConfigType, error) {
defs := &v1beta1.ComponentDefinitionList{}
if err := u.kubeClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig}); err != nil {
client.MatchingLabels{definition.UserPrefix + "catalog.config.oam.dev": types.VelaCoreConfig}); err != nil {
return nil, err
}
@@ -134,51 +136,59 @@ func (u *configUseCaseImpl) GetConfigType(ctx context.Context, configType string
}
func (u *configUseCaseImpl) CreateConfig(ctx context.Context, req apis.CreateConfigRequest) error {
app := v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: req.Name,
Namespace: types.DefaultKubeVelaNS,
Annotations: map[string]string{
types.AnnotationConfigAlias: req.Alias,
},
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigType: req.ComponentType,
types.LabelConfigProject: req.Project,
},
},
Spec: v1beta1.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: req.Name,
Type: req.ComponentType,
Properties: &runtime.RawExtension{Raw: []byte(req.Properties)},
},
},
},
p := req.Properties
// If the component is Terraform type, set the provider name same as the application name and the component name
if strings.HasPrefix(req.ComponentType, types.TerraformComponentPrefix) {
var properties map[string]interface{}
if err := json.Unmarshal([]byte(p), &properties); err != nil {
return errors.Wrapf(err, "unable to process the properties of %s", req.ComponentType)
}
properties["name"] = req.Name
tmp, err := json.Marshal(properties)
if err != nil {
return errors.Wrapf(err, "unable to process the properties of %s", req.ComponentType)
}
p = string(tmp)
}
return u.kubeClient.Create(ctx, &app)
ui := config.UIParam{
Alias: req.Alias,
Description: req.Description,
Project: req.Project,
}
return config.CreateApplication(ctx, u.kubeClient, req.Name, req.ComponentType, p, ui)
}
func (u *configUseCaseImpl) GetConfigs(ctx context.Context, configType string) ([]*apis.Config, error) {
switch configType {
case types.TerraformProvider:
defs := &v1beta1.ComponentDefinitionList{}
if err := u.kubeClient.List(ctx, defs, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definition.UserPrefix + "type.config.oam.dev": types.TerraformProvider,
}); err != nil {
providers, err := config.ListTerraformProviders(ctx, u.kubeClient)
if err != nil {
return nil, err
}
var configs []*apis.Config
for _, d := range defs.Items {
subConfigs, err := u.getConfigsByConfigType(ctx, d.Name)
if err != nil {
configs := make([]*apis.Config, len(providers))
for i, p := range providers {
var a v1beta1.Application
if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: p.Name}, &a); err != nil {
if kerrors.IsNotFound(err) {
t := p.CreationTimestamp.Time
configs[i] = &apis.Config{
Name: p.Name,
CreatedTime: &t,
}
if p.Status.State == terraformtypes.ProviderIsReady {
configs[i].Status = configIsReady
} else {
configs[i].Status = configIsNotReady
}
continue
}
return nil, err
}
configs = append(configs, subConfigs...)
// If the application doesn't have any components, skip it as something wrong happened.
if !strings.HasPrefix(a.Labels[types.LabelConfigType], types.TerraformComponentPrefix) {
continue
}
configs[i] = retrieveConfigFromApplication(a, a.Labels[types.LabelConfigProject])
}
return configs, nil
@@ -193,7 +203,7 @@ func (u *configUseCaseImpl) getConfigsByConfigType(ctx context.Context, configTy
if err := u.kubeClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigType: configType,
}); err != nil {
return nil, err
@@ -201,18 +211,7 @@ func (u *configUseCaseImpl) getConfigsByConfigType(ctx context.Context, configTy
configs := make([]*apis.Config, len(apps.Items))
for i, a := range apps.Items {
configs[i] = &apis.Config{
ConfigType: a.Labels[types.LabelConfigType],
Name: a.Name,
Project: a.Labels[types.LabelConfigProject],
CreatedTime: &(a.CreationTimestamp.Time),
}
switch a.Status.Phase {
case common.ApplicationRunning:
configs[i].Status = configIsReady
default:
configs[i].Status = configIsNotReady
}
configs[i] = retrieveConfigFromApplication(a, a.Labels[types.LabelConfigProject])
}
return configs, nil
}
@@ -234,11 +233,11 @@ func (u *configUseCaseImpl) GetConfig(ctx context.Context, configType, name stri
}
func (u *configUseCaseImpl) DeleteConfig(ctx context.Context, configType, name string) error {
var a = &v1beta1.Application{}
if err := u.kubeClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, a); err != nil {
return err
var isTerraformProvider bool
if strings.HasPrefix(configType, types.TerraformComponentPrefix) {
isTerraformProvider = true
}
return u.kubeClient.Delete(ctx, a)
return config.DeleteApplication(ctx, u.kubeClient, name, isTerraformProvider)
}
// ApplicationDeployTarget is the struct of application deploy target
@@ -254,7 +253,7 @@ func SyncConfigs(ctx context.Context, k8sClient client.Client, project string, t
var secrets v1.SecretList
if err := k8sClient.List(ctx, &secrets, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigSyncToMultiCluster: "true",
}); err != nil {
return err
@@ -313,7 +312,7 @@ func SyncConfigs(ctx context.Context, k8sClient client.Client, project string, t
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigProject: project,
},
},

View File

@@ -22,8 +22,11 @@ import (
"sort"
"strings"
"testing"
"time"
. "github.com/agiledragon/gomonkey/v2"
terraformtypes "github.com/oam-dev/terraform-controller/api/types"
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
"gotest.tools/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -32,6 +35,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
@@ -53,7 +57,7 @@ func TestListConfigTypes(t *testing.T) {
Name: "def1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definition.UserPrefix + "catalog.config.oam.dev": types.VelaCoreConfig,
definitionType: types.TerraformProvider,
},
},
@@ -70,7 +74,7 @@ func TestListConfigTypes(t *testing.T) {
definitionAlias: "Def2",
},
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definition.UserPrefix + "catalog.config.oam.dev": types.VelaCoreConfig,
},
},
}
@@ -150,7 +154,7 @@ func TestGetConfigType(t *testing.T) {
definitionAlias: "Def2",
},
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definition.UserPrefix + "catalog.config.oam.dev": types.VelaCoreConfig,
},
},
}
@@ -236,6 +240,11 @@ func TestCreateConfig(t *testing.T) {
ctx := context.Background()
properties, err := json.Marshal(map[string]interface{}{
"name": "default",
})
assert.NilError(t, err)
testcases := []struct {
name string
args args
@@ -252,6 +261,18 @@ func TestCreateConfig(t *testing.T) {
},
},
},
{
name: "create terraform-alibaba config",
args: args{
h: h,
req: apis.CreateConfigRequest{
Name: "n1",
ComponentType: "terraform-alibaba",
Project: "p1",
Properties: string(properties),
},
},
},
}
for _, tc := range testcases {
@@ -268,37 +289,53 @@ func TestGetConfigs(t *testing.T) {
s := runtime.NewScheme()
v1beta1.AddToScheme(s)
corev1.AddToScheme(s)
def1 := &v1beta1.ComponentDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "ComponentDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
terraformapi.AddToScheme(s)
createdTime, _ := time.Parse(time.UnixDate, "Wed Apr 7 11:06:39 PST 2022")
provider1 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "def1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
definitionType: types.TerraformProvider,
},
Name: "provider1",
Namespace: "default",
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: terraformapi.ProviderStatus{
State: terraformtypes.ProviderIsReady,
},
}
def2 := &v1beta1.ComponentDefinition{
TypeMeta: metav1.TypeMeta{
Kind: "ComponentDefinition",
APIVersion: "core.oam.dev/v1beta1",
},
provider2 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "def2",
Namespace: types.DefaultKubeVelaNS,
Annotations: map[string]string{
definitionAlias: "Def2",
},
Labels: map[string]string{
definition.UserPrefix + "catalog.config.oam.dev": velaCoreConfig,
},
Name: "provider2",
Namespace: "default",
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: terraformapi.ProviderStatus{
State: terraformtypes.ProviderIsNotReady,
},
}
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(def1, def2).Build()
provider3 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "provider3",
Namespace: "default",
},
}
app1 := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "provider3",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigType: "terraform-alibaba",
},
CreationTimestamp: metav1.NewTime(createdTime),
},
Status: common.AppStatus{
Phase: common.ApplicationRendering,
},
}
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(provider1, provider2, provider3, app1).Build()
h := &configUseCaseImpl{kubeClient: k8sClient}
@@ -326,7 +363,25 @@ func TestGetConfigs(t *testing.T) {
h: h,
},
want: want{
configs: nil,
configs: []*apis.Config{
{
Name: "provider1",
CreatedTime: &createdTime,
Status: "Ready",
},
{
Name: "provider2",
CreatedTime: &createdTime,
Status: "Not ready",
},
{
Name: "provider3",
CreatedTime: &createdTime,
Status: "Not ready",
ConfigType: "terraform-alibaba",
ApplicationStatus: common.ApplicationRendering,
},
},
},
},
}
@@ -502,7 +557,7 @@ func TestSyncConfigs(t *testing.T) {
Name: "s1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigProject: "p1",
types.LabelConfigSyncToMultiCluster: "true",
},
@@ -587,3 +642,129 @@ func TestSyncConfigs(t *testing.T) {
})
}
}
func TestDeleteConfig(t *testing.T) {
s := runtime.NewScheme()
v1beta1.AddToScheme(s)
corev1.AddToScheme(s)
terraformapi.AddToScheme(s)
provider1 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "p1",
Namespace: "default",
},
}
provider2 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "p2",
Namespace: "default",
},
}
provider3 := &terraformapi.Provider{
ObjectMeta: metav1.ObjectMeta{
Name: "p3",
Namespace: "default",
},
}
app1 := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "config-terraform-provider-p1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigType: "terraform-alibaba",
},
},
}
app2 := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "p2",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigType: "terraform-alibaba",
},
},
}
normalApp := &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: "a9",
Namespace: types.DefaultKubeVelaNS,
},
}
k8sClient := fake.NewClientBuilder().WithScheme(s).WithObjects(provider1, provider2, provider3, app1, app2, normalApp).Build()
h := &configUseCaseImpl{kubeClient: k8sClient}
type args struct {
configType string
name string
h ConfigHandler
}
type want struct {
errMsg string
}
ctx := context.Background()
testcases := []struct {
name string
args args
want want
}{
{
name: "delete a legacy terraform provider",
args: args{
configType: "terraform-alibaba",
name: "p1",
h: h,
},
want: want{},
},
{
name: "delete a terraform provider",
args: args{
configType: "terraform-alibaba",
name: "p2",
h: h,
},
want: want{},
},
{
name: "delete a terraform provider, but its application not found",
args: args{
configType: "terraform-alibaba",
name: "p3",
h: h,
},
want: want{
errMsg: "could not be disabled because it was created by enabling a Terraform provider or was manually created",
},
},
{
name: "delete a normal config, but failed",
args: args{
configType: "config-image-registry",
name: "a10",
h: h,
},
want: want{
errMsg: "not found",
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
err := tc.args.h.DeleteConfig(ctx, tc.args.configType, tc.args.name)
if tc.want.errMsg != "" || err != nil {
assert.ErrorContains(t, err, tc.want.errMsg)
}
})
}
}

View File

@@ -20,6 +20,8 @@ import (
"context"
"strconv"
"github.com/oam-dev/kubevela/pkg/utils/config"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
"github.com/oam-dev/kubevela/pkg/apiserver/log"
@@ -31,7 +33,6 @@ import (
"github.com/oam-dev/kubevela/pkg/utils/helm"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types2 "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -133,41 +134,20 @@ func (d defaultHelmHandler) ListChartRepo(ctx context.Context, projectName strin
var res []*v1.ChartRepoResponse
var err error
if len(projectName) != 0 {
projectSecrets := corev1.SecretList{}
opts := []client.ListOption{
client.MatchingLabels{oam.LabelConfigType: "config-helm-repository", types.LabelConfigProject: projectName},
client.InNamespace(types.DefaultKubeVelaNS),
}
err = d.k8sClient.List(ctx, &projectSecrets, opts...)
if err != nil {
return nil, err
}
for _, item := range projectSecrets.Items {
res = append(res, &v1.ChartRepoResponse{URL: string(item.Data["url"]), SecretName: item.Name})
}
projectSecrets := corev1.SecretList{}
opts := []client.ListOption{
client.MatchingLabels{oam.LabelConfigType: "config-helm-repository"},
client.InNamespace(types.DefaultKubeVelaNS),
}
globalSecrets := corev1.SecretList{}
selector := metav1.LabelSelector{
MatchLabels: map[string]string{oam.LabelConfigType: "config-helm-repository"},
MatchExpressions: []metav1.LabelSelectorRequirement{
{Key: types.LabelConfigProject, Operator: metav1.LabelSelectorOpDoesNotExist},
},
}
ls, _ := metav1.LabelSelectorAsSelector(&selector)
err = d.k8sClient.List(ctx, &globalSecrets, &client.ListOptions{
LabelSelector: ls,
Namespace: types.DefaultKubeVelaNS,
})
err = d.k8sClient.List(ctx, &projectSecrets, opts...)
if err != nil {
return nil, err
}
for _, item := range globalSecrets.Items {
res = append(res, &v1.ChartRepoResponse{URL: string(item.Data["url"]), SecretName: item.Name})
for _, item := range projectSecrets.Items {
if config.ProjectMatched(item.DeepCopy(), projectName) {
res = append(res, &v1.ChartRepoResponse{URL: string(item.Data["url"]), SecretName: item.Name})
}
}
return &v1.ChartRepoResponseList{ChartRepoResponse: res}, nil

View File

@@ -338,6 +338,7 @@ kind: Secret
metadata:
labels:
config.oam.dev/type: config-helm-repository
config.oam.dev/project: ""
name: global-helm-repo
namespace: vela-system
type: Opaque

View File

@@ -487,7 +487,7 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
if err := p.k8sClient.List(ctx, apps, client.InNamespace(types.DefaultKubeVelaNS),
client.MatchingLabels{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: velaCoreConfig,
types.LabelConfigCatalog: types.VelaCoreConfig,
}); err != nil {
return nil, err
}
@@ -499,7 +499,7 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
return nil, err
}
for _, p := range providers.Items {
if p.Labels[types.LabelConfigCatalog] == velaCoreConfig {
if p.Labels[types.LabelConfigCatalog] == types.VelaCoreConfig {
continue
}
t := p.CreationTimestamp.Time
@@ -520,16 +520,10 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
for _, a := range apps.Items {
appProject := a.Labels[types.LabelConfigProject]
if a.Status.Phase != common.ApplicationRunning || (appProject != "" && appProject != projectName) ||
!strings.Contains(a.Labels[types.LabelConfigType], "terraform-") {
!strings.Contains(a.Labels[types.LabelConfigType], types.TerraformComponentPrefix) {
continue
}
configs = append(configs, &apisv1.Config{
ConfigType: a.Labels[types.LabelConfigType],
Name: a.Name,
Project: appProject,
CreatedTime: &(a.CreationTimestamp.Time),
ApplicationStatus: a.Status.Phase,
})
configs = append(configs, retrieveConfigFromApplication(a, appProject))
}
configs = append(configs, legacyTerraformProviders...)
@@ -539,13 +533,7 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
if appProject != "" && appProject != projectName {
continue
}
configs = append(configs, &apisv1.Config{
ConfigType: a.Labels[types.LabelConfigType],
Name: a.Name,
Project: appProject,
CreatedTime: &(a.CreationTimestamp.Time),
ApplicationStatus: a.Status.Phase,
})
configs = append(configs, retrieveConfigFromApplication(a, appProject))
}
configs = append(configs, legacyTerraformProviders...)
case types.DexConnector, types.HelmRepository, types.ImageRegistry:
@@ -556,13 +544,7 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
continue
}
if a.Labels[types.LabelConfigType] == t {
configs = append(configs, &apisv1.Config{
ConfigType: a.Labels[types.LabelConfigType],
Name: a.Name,
Project: appProject,
CreatedTime: &(a.CreationTimestamp.Time),
ApplicationStatus: a.Status.Phase,
})
configs = append(configs, retrieveConfigFromApplication(a, appProject))
}
}
default:
@@ -579,13 +561,6 @@ func (p *projectUsecaseImpl) GetConfigs(ctx context.Context, projectName, config
configs[i].ConfigTypeAlias = d.Annotations[definitionAlias]
}
}
if c.ApplicationStatus != "" {
if c.ApplicationStatus == common.ApplicationRunning {
configs[i].Status = configIsReady
} else {
configs[i].Status = configIsNotReady
}
}
}
return configs, nil
}
@@ -616,3 +591,25 @@ func ConvertProjectUserModel2Base(user *model.ProjectUser) *apisv1.ProjectUserBa
}
return base
}
func retrieveConfigFromApplication(a v1beta1.Application, project string) *apisv1.Config {
var (
applicationStatus = a.Status.Phase
status string
)
if applicationStatus == common.ApplicationRunning {
status = configIsReady
} else {
status = configIsNotReady
}
return &apisv1.Config{
ConfigType: a.Labels[types.LabelConfigType],
Name: a.Name,
Project: project,
CreatedTime: &(a.CreationTimestamp.Time),
ApplicationStatus: applicationStatus,
Status: status,
Alias: a.Annotations[types.AnnotationConfigAlias],
Description: a.Annotations[types.AnnotationConfigDescription],
}
}

View File

@@ -293,7 +293,7 @@ func TestProjectGetConfigs(t *testing.T) {
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
velatypes.LabelConfigCatalog: velaCoreConfig,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: "terraform-provider",
"config.oam.dev/project": "p1",
},
@@ -308,7 +308,7 @@ func TestProjectGetConfigs(t *testing.T) {
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
velatypes.LabelConfigCatalog: velaCoreConfig,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: "terraform-provider",
},
CreationTimestamp: metav1.NewTime(createdTime),
@@ -322,7 +322,7 @@ func TestProjectGetConfigs(t *testing.T) {
Namespace: velatypes.DefaultKubeVelaNS,
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
velatypes.LabelConfigCatalog: velaCoreConfig,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
velatypes.LabelConfigType: "dex-connector",
"config.oam.dev/project": "p3",
},
@@ -347,7 +347,7 @@ func TestProjectGetConfigs(t *testing.T) {
Name: "provider2",
Namespace: "default",
Labels: map[string]string{
velatypes.LabelConfigCatalog: velaCoreConfig,
velatypes.LabelConfigCatalog: velatypes.VelaCoreConfig,
},
},
Status: terraformapi.ProviderStatus{

View File

@@ -21,6 +21,7 @@ import (
"errors"
"fmt"
"reflect"
"time"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -81,6 +82,7 @@ func (u systemInfoUsecaseImpl) Get(ctx context.Context) (*model.SystemInfo, erro
info.InstallID = installID
info.EnableCollection = true
info.LoginType = model.LoginTypeLocal
info.BaseModel = model.BaseModel{CreateTime: time.Now()}
err = u.ds.Add(ctx, info)
if err != nil {
return nil, err
@@ -100,6 +102,16 @@ func (u systemInfoUsecaseImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInf
VelaVersion: version.VelaVersion,
GitVersion: version.GitRevision,
},
StatisticInfo: v1.StatisticInfo{
AppCount: info.StatisticInfo.AppCount,
ClusterCount: info.StatisticInfo.ClusterCount,
EnableAddonList: info.StatisticInfo.EnabledAddon,
ComponentDefinitionTopList: info.StatisticInfo.TopKCompDef,
TraitDefinitionTopList: info.StatisticInfo.TopKTraitDef,
WorkflowDefinitionTopList: info.StatisticInfo.TopKWorkflowStepDef,
PolicyDefinitionTopList: info.StatisticInfo.TopKPolicyDef,
UpdateTime: info.StatisticInfo.UpdateTime,
},
}, nil
}
@@ -114,7 +126,9 @@ func (u systemInfoUsecaseImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1.
LoginType: sysInfo.LoginType,
BaseModel: model.BaseModel{
CreateTime: info.CreateTime,
UpdateTime: time.Now(),
},
StatisticInfo: info.StatisticInfo,
}
if sysInfo.LoginType == model.LoginTypeDex {
@@ -135,9 +149,11 @@ func (u systemInfoUsecaseImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1.
}
return &v1.SystemInfoResponse{
SystemInfo: v1.SystemInfo{
InstallID: modifiedInfo.InstallID,
PlatformID: modifiedInfo.InstallID,
EnableCollection: modifiedInfo.EnableCollection,
LoginType: modifiedInfo.LoginType,
// always use the initial createTime as system's installTime
InstallTime: info.CreateTime,
},
SystemVersion: v1.SystemVersion{VelaVersion: version.VelaVersion, GitVersion: version.GitRevision},
}, nil
@@ -155,9 +171,10 @@ func (u systemInfoUsecaseImpl) Init(ctx context.Context) error {
func convertInfoToBase(info *model.SystemInfo) v1.SystemInfo {
return v1.SystemInfo{
InstallID: info.InstallID,
PlatformID: info.InstallID,
EnableCollection: info.EnableCollection,
LoginType: info.LoginType,
InstallTime: info.CreateTime,
}
}

View File

@@ -296,7 +296,7 @@ func (s *enabledAddonWebService) GetWebService() *restful.WebService {
Filter(s.rbacUsecase.CheckPerm("addon", "list")).
Param(ws.QueryParameter("registry", "filter addons from given registry").DataType("string")).
Param(ws.QueryParameter("query", "Fuzzy search based on name and description.").DataType("string")).
Returns(200, "OK", apis.ListAddonResponse{}).
Returns(200, "OK", apis.ListEnabledAddonResponse{}).
Returns(400, "Bad Request", bcode.Bcode{}).
Writes(apis.ListAddonResponse{}))

View File

@@ -18,8 +18,9 @@ package cmd
import (
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/term"
cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
)
// Builder build command with factory

View File

@@ -17,8 +17,9 @@ limitations under the License.
package cmd
import (
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/controller-runtime/pkg/client"
cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
)
// Factory client factory for running command

32
pkg/cmd/util/helpers.go Normal file
View File

@@ -0,0 +1,32 @@
/*
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/pkg/errors"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
)
// CheckErr wraps the kubectl CheckErr func by preventing inappropriate type conversion and panic
func CheckErr(err error) {
defer func() {
if r := recover(); r != nil {
cmdutil.CheckErr(errors.New(err.Error()))
}
}()
cmdutil.CheckErr(err)
}

View File

@@ -18,9 +18,9 @@ package cmd
import (
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"github.com/oam-dev/kubevela/apis/types"
cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/env"
)

View File

@@ -117,8 +117,16 @@ func convertStepProperties(step *v1beta1.WorkflowStep, app *v1beta1.Application)
return err
}
var componentNames []string
for _, c := range app.Spec.Components {
componentNames = append(componentNames, c.Name)
}
for _, c := range app.Spec.Components {
if c.Name == o.Component {
if dcName, ok := checkDependsOnValidComponent(c.DependsOn, componentNames); !ok {
return errors.Errorf("component %s not found, which is depended by %s", dcName, c.Name)
}
step.Inputs = append(step.Inputs, c.Inputs...)
for index := range step.Inputs {
parameterKey := strings.TrimSpace(step.Inputs[index].ParameterKey)
@@ -135,11 +143,23 @@ func convertStepProperties(step *v1beta1.WorkflowStep, app *v1beta1.Application)
step.Properties = util.Object2RawExtension(c)
return nil
}
}
return errors.Errorf("component %s not found", o.Component)
}
func checkDependsOnValidComponent(dependsOnComponentNames, allComponentNames []string) (string, bool) {
// does not depends on other components
if dependsOnComponentNames == nil {
return "", true
}
for _, dc := range dependsOnComponentNames {
if !utils.StringsContain(allComponentNames, dc) {
return dc, false
}
}
return "", true
}
func (h *AppHandler) renderComponentFunc(appParser *appfile.Parser, appRev *v1beta1.ApplicationRevision, af *appfile.Appfile) oamProvider.ComponentRender {
return func(comp common.ApplicationComponent, patcher *value.Value, clusterName string, overrideNamespace string, env string) (*unstructured.Unstructured, []*unstructured.Unstructured, error) {
ctx := multicluster.ContextWithClusterName(context.Background(), clusterName)

View File

@@ -240,4 +240,118 @@ var _ = Describe("Test Application workflow generator", func() {
_, _, err = renderFunc(comp, nil, "", "", "")
Expect(err).Should(BeNil())
})
It("Test generate application workflow with dependsOn", func() {
app := &oamcore.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "app-with-input-output",
Namespace: namespaceName,
},
Spec: oamcore.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: "myweb1",
Type: "worker-with-health",
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
},
{
Name: "myweb2",
Type: "worker-with-health",
DependsOn: []string{"myweb1"},
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
},
},
},
}
af, err := appParser.GenerateAppFile(ctx, app)
Expect(err).Should(BeNil())
appRev := &oamcore.ApplicationRevision{}
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
Expect(err).Should(Succeed())
taskRunner, err := handler.GenerateApplicationSteps(ctx, app, appParser, af, appRev)
Expect(err).To(BeNil())
Expect(len(taskRunner)).Should(BeEquivalentTo(2))
Expect(taskRunner[0].Name()).Should(BeEquivalentTo("myweb1"))
Expect(taskRunner[1].Name()).Should(BeEquivalentTo("myweb2"))
})
It("Test generate application workflow with invalid dependsOn", func() {
app := &oamcore.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "app-with-input-output",
Namespace: namespaceName,
},
Spec: oamcore.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: "myweb1",
Type: "worker-with-health",
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
},
{
Name: "myweb2",
Type: "worker-with-health",
DependsOn: []string{"myweb0"},
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
},
},
},
}
af, err := appParser.GenerateAppFile(ctx, app)
Expect(err).Should(BeNil())
appRev := &oamcore.ApplicationRevision{}
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
Expect(err).Should(Succeed())
_, err = handler.GenerateApplicationSteps(ctx, app, appParser, af, appRev)
Expect(err).NotTo(BeNil())
})
It("Test generate application workflow with multiple invalid dependsOn", func() {
app := &oamcore.Application{
TypeMeta: metav1.TypeMeta{
Kind: "Application",
APIVersion: "core.oam.dev/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "app-with-input-output",
Namespace: namespaceName,
},
Spec: oamcore.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: "myweb1",
Type: "worker-with-health",
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
},
{
Name: "myweb2",
Type: "worker-with-health",
DependsOn: []string{"myweb1", "myweb0", "myweb3"},
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox","lives": "i am lives","enemies": "empty"}`)},
},
},
},
}
af, err := appParser.GenerateAppFile(ctx, app)
Expect(err).Should(BeNil())
appRev := &oamcore.ApplicationRevision{}
handler, err := NewAppHandler(ctx, reconciler, app, appParser)
Expect(err).Should(Succeed())
_, err = handler.GenerateApplicationSteps(ctx, app, appParser, af, appRev)
Expect(err).NotTo(BeNil())
})
})

View File

@@ -755,6 +755,7 @@ func (h *AppHandler) FinalizeAndApplyAppRevision(ctx context.Context) error {
appRev.SetGroupVersionKind(v1beta1.ApplicationRevisionGroupVersionKind)
// pass application's annotations & labels to app revision
appRev.SetAnnotations(h.app.GetAnnotations())
delete(appRev.Annotations, oam.AnnotationLastAppliedConfiguration)
appRev.SetLabels(h.app.GetLabels())
util.AddLabels(appRev, map[string]string{
oam.LabelAppName: h.app.GetName(),

View File

@@ -29,12 +29,15 @@ const (
LegacyObjectTypeIdentifier featuregate.Feature = "LegacyObjectTypeIdentifier"
// DeprecatedObjectLabelSelector enable the use of deprecated object label selector for selecting ref-object
DeprecatedObjectLabelSelector featuregate.Feature = "DeprecatedObjectLabelSelector"
// LegacyResourceTrackerGC enable the gc of legacy resource tracker in managed clusters
LegacyResourceTrackerGC featuregate.Feature = "LegacyResourceTrackerGC"
)
var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
DeprecatedPolicySpec: {Default: false, PreRelease: featuregate.Alpha},
LegacyObjectTypeIdentifier: {Default: false, PreRelease: featuregate.Alpha},
DeprecatedObjectLabelSelector: {Default: false, PreRelease: featuregate.Alpha},
LegacyResourceTrackerGC: {Default: true, PreRelease: featuregate.Alpha},
}
func init() {

View File

@@ -35,6 +35,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
velatypes "github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/utils/common"
errors3 "github.com/oam-dev/kubevela/pkg/utils/errors"
@@ -46,7 +47,7 @@ const (
// ClusterContextKey is the name of cluster using in client http context
ClusterContextKey = contextKey("ClusterName")
// ClusterLocalName specifies the local cluster
ClusterLocalName = "local"
ClusterLocalName = velatypes.ClusterLocalName
)
var (

View File

@@ -30,10 +30,12 @@ import (
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/features"
"github.com/oam-dev/kubevela/pkg/monitor/metrics"
"github.com/oam-dev/kubevela/pkg/multicluster"
"github.com/oam-dev/kubevela/pkg/oam"
@@ -319,6 +321,10 @@ func (h *gcHandler) GarbageCollectComponentRevisionResourceTracker(ctx context.C
const velaVersionNumberToUpgradeResourceTracker = "v1.2.0"
func (h *gcHandler) GarbageCollectLegacyResourceTrackers(ctx context.Context) error {
// skip legacy gc if controller not enable this feature
if !utilfeature.DefaultMutableFeatureGate.Enabled(features.LegacyResourceTrackerGC) {
return nil
}
// skip legacy gc if application is not handled by new version rt
if h.app.GetDeletionTimestamp() == nil && h.resourceKeeper._currentRT == nil {
return nil

View File

@@ -0,0 +1,160 @@
/*
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 config
import (
"context"
"fmt"
"strings"
tcv1beta1 "github.com/oam-dev/terraform-controller/api/v1beta1"
"github.com/pkg/errors"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/apiserver/model"
)
const (
errAuthenticateProvider = "failed to authenticate Terraform cloud provider %s for %s"
errProviderExists = "terraform provider %s for %s already exists"
errDeleteProvider = "failed to delete Terraform Provider %s"
errCouldNotDeleteProvider = "the Terraform Provider %s could not be disabled because it was created by enabling a Terraform provider or was manually created"
errCheckProviderExistence = "failed to check if Terraform Provider %s exists"
)
// UIParam is the UI parameters from VelaUX for the application
type UIParam struct {
Alias string `json:"alias"`
Description string `json:"description"`
Project string `json:"project"`
}
// CreateApplication creates a new application for the config
func CreateApplication(ctx context.Context, k8sClient client.Client, name, componentType, properties string, ui UIParam) error {
if strings.HasPrefix(componentType, types.TerraformComponentPrefix) {
existed, err := IsTerraformProviderExisted(ctx, k8sClient, name)
if err != nil {
return errors.Wrapf(err, errAuthenticateProvider, name, componentType)
}
if existed {
return fmt.Errorf(errProviderExists, name, componentType)
}
}
app := v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: types.DefaultKubeVelaNS,
Annotations: map[string]string{
types.AnnotationConfigAlias: ui.Alias,
types.AnnotationConfigDescription: ui.Description,
},
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
types.LabelConfigCatalog: types.VelaCoreConfig,
types.LabelConfigType: componentType,
types.LabelConfigProject: ui.Project,
},
},
Spec: v1beta1.ApplicationSpec{
Components: []common.ApplicationComponent{
{
Name: name,
Type: componentType,
Properties: &runtime.RawExtension{Raw: []byte(properties)},
},
},
},
}
return k8sClient.Create(ctx, &app)
}
// DeleteApplication deletes a config application, including a Terraform provider.
// For a Terraform Provider, it can come from
// 1). manually created a Terraform Provider object, like https://github.com/oam-dev/terraform-controller/blob/master/getting-started.md#aws
// 2). by enabling a Terraform provider addon in version older than v1.3.0
// 3). by create a Terraform provider via `vela provider add`
// 4). by VelaUX
// We will only target on deleting a provider which comes from 3) or 4) as for 1), it can be easily delete by hand, and
// for 2), it will be recreated by the addon.
func DeleteApplication(ctx context.Context, k8sClient client.Client, name string, isTerraformProvider bool) error {
if isTerraformProvider {
existed, err := IsTerraformProviderExisted(ctx, k8sClient, name)
if err != nil {
return errors.Wrapf(err, errCheckProviderExistence, name)
}
if existed {
// In version 1.3.0, we used `providerAppName` as the name of the application to create a provider, but
// in version 1.3.1, a config name is the config name, ie, the provider name. To keep backward compatibility,
// we need to check the legacy name and the current name of the application.
legacyName := fmt.Sprintf("%s-%s", types.ProviderAppPrefix, name)
err1 := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: legacyName}, &v1beta1.Application{})
if err1 == nil {
name = legacyName
}
if err1 != nil {
if kerrors.IsNotFound(err1) {
err2 := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, &v1beta1.Application{})
if err2 != nil {
if kerrors.IsNotFound(err2) {
return fmt.Errorf(errCouldNotDeleteProvider, name)
}
return fmt.Errorf(errDeleteProvider, name)
}
}
return fmt.Errorf(errDeleteProvider, name)
}
}
}
a := &v1beta1.Application{}
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: name}, a); err != nil {
return err
}
if err := k8sClient.Delete(ctx, a); err != nil {
return err
}
return nil
}
// ListTerraformProviders returns a list of Terraform providers.
func ListTerraformProviders(ctx context.Context, k8sClient client.Client) ([]tcv1beta1.Provider, error) {
l := &tcv1beta1.ProviderList{}
if err := k8sClient.List(ctx, l, client.InNamespace(types.ProviderNamespace)); err != nil {
return nil, err
}
return l.Items, nil
}
// IsTerraformProviderExisted returns whether a Terraform provider exists.
func IsTerraformProviderExisted(ctx context.Context, k8sClient client.Client, name string) (bool, error) {
l, err := ListTerraformProviders(ctx, k8sClient)
if err != nil {
return false, err
}
for _, p := range l {
if p.Name == name {
return true, nil
}
}
return false, nil
}

View File

@@ -0,0 +1,31 @@
/*
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 config
import (
v1 "k8s.io/api/core/v1"
"github.com/oam-dev/kubevela/apis/types"
)
// ProjectMatched will check whether a config secret can be used in a given project
func ProjectMatched(s *v1.Secret, project string) bool {
if s.Labels[types.LabelConfigProject] == "" || s.Labels[types.LabelConfigProject] == project {
return true
}
return false
}

View File

@@ -0,0 +1,97 @@
/*
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 config
import (
"testing"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"github.com/oam-dev/kubevela/apis/types"
)
var ResponseString = "Hello HTTP Get."
func TestMatchProject(t *testing.T) {
s := runtime.NewScheme()
corev1.AddToScheme(s)
secret1 := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "s1",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigProject: "p1",
},
},
}
secret2 := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "s2",
Namespace: types.DefaultKubeVelaNS,
Labels: map[string]string{
types.LabelConfigProject: "",
},
},
}
type args struct {
secret *corev1.Secret
project string
}
type want struct {
matched bool
}
testcases := []struct {
name string
args args
want want
}{
{
name: "matched",
args: args{
project: "p99",
secret: secret1,
},
want: want{
matched: false,
},
},
{
name: "not matched",
args: args{
project: "p99",
secret: secret2,
},
want: want{
matched: true,
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
got := ProjectMatched(tc.args.secret, tc.args.project)
assert.Equal(t, tc.want.matched, got)
})
}
}

View File

@@ -136,6 +136,7 @@ func (c *AppCollector) FindResourceFromResourceTrackerSpec(app *v1beta1.Applicat
ctx := context.Background()
rootRT, currentRT, historyRTs, _, err := resourcetracker.ListApplicationResourceTrackers(ctx, c.k8sClient, app)
if err != nil {
klog.Errorf("query the resourcetrackers failure %s", err.Error())
return nil, err
}
var resources = []Resource{}
@@ -151,11 +152,19 @@ func (c *AppCollector) FindResourceFromResourceTrackerSpec(app *v1beta1.Applicat
existResources[managedResource.ClusterObjectReference] = true
obj, err := managedResource.ToUnstructuredWithData()
if err != nil {
klog.Errorf("get obj from resource tracker failure %s", err.Error())
continue
// For the application with apply once policy, there is no data in RT.
_, obj, err = getObjectCreatedByComponent(c.k8sClient, managedResource.ObjectReference, managedResource.Cluster)
if err != nil {
klog.Errorf("get obj from the cluster failure %s", err.Error())
continue
}
}
clusterName := managedResource.Cluster
if clusterName == "" {
clusterName = multicluster.ClusterLocalName
}
resources = append(resources, Resource{
Cluster: managedResource.Cluster,
Cluster: clusterName,
Revision: oam.GetPublishVersion(rt),
Component: managedResource.Component,
Object: obj,

View File

@@ -31,6 +31,7 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/klog"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
@@ -83,7 +84,8 @@ var _ = Describe("Test Query Provider", func() {
Name: "test",
Namespace: "test",
Annotations: map[string]string{
"oam.dev/kubevela-version": "v1.2.0-beta.2",
oam.AnnotationKubeVelaVersion: "v1.3.1",
oam.AnnotationPublishVersion: "v1",
},
},
Spec: v1beta1.ApplicationSpec{
@@ -154,6 +156,53 @@ var _ = Describe("Test Query Provider", func() {
})
Expect(k8sClient.Create(ctx, appService)).Should(BeNil())
rt := &v1beta1.ResourceTracker{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-v1-%s", oldApp.Name, oldApp.Namespace),
Labels: map[string]string{
oam.LabelAppName: oldApp.Name,
oam.LabelAppNamespace: oldApp.Namespace,
},
Annotations: map[string]string{
oam.AnnotationPublishVersion: "v1",
},
},
Spec: v1beta1.ResourceTrackerSpec{
ManagedResources: []v1beta1.ManagedResource{
{
ClusterObjectReference: common.ClusterObjectReference{
Cluster: "",
ObjectReference: corev1.ObjectReference{
APIVersion: "v1",
Kind: "Service",
Namespace: namespace,
Name: "web",
},
},
OAMObjectReference: common.OAMObjectReference{
Component: "web",
},
},
{
ClusterObjectReference: common.ClusterObjectReference{
Cluster: "",
ObjectReference: corev1.ObjectReference{
APIVersion: "apps/v1",
Kind: "Deployment",
Namespace: namespace,
Name: "web",
},
},
OAMObjectReference: common.OAMObjectReference{
Component: "web",
},
},
},
Type: v1beta1.ResourceTrackerTypeVersioned,
},
}
Expect(k8sClient.Create(ctx, rt)).Should(BeNil())
prd := provider{cli: k8sClient}
opt := `app: {
name: "test"
@@ -170,6 +219,9 @@ var _ = Describe("Test Query Provider", func() {
appResList := new(AppResourcesList)
Expect(v.UnmarshalTo(appResList)).Should(BeNil())
if appResList.Err != "" {
klog.Error(appResList.Err)
}
Expect(len(appResList.List)).Should(Equal(2))
@@ -180,7 +232,7 @@ var _ = Describe("Test Query Provider", func() {
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(&app), updateApp)).Should(BeNil())
updateApp.ObjectMeta.Annotations = map[string]string{
"oam.dev/kubevela-version": "master",
oam.AnnotationKubeVelaVersion: "v1.1.0",
}
Expect(k8sClient.Update(ctx, updateApp)).Should(BeNil())
newValue, err := value.NewValue(opt, nil, "")

View File

@@ -19,7 +19,7 @@ package template
import (
"context"
"embed"
"path/filepath"
"fmt"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
@@ -60,7 +60,8 @@ func (loader *WorkflowStepLoader) LoadTaskTemplate(ctx context.Context, name str
staticFilename := name + ".cue"
for _, file := range files {
if staticFilename == file.Name() {
content, err := templateFS.ReadFile(filepath.Join(templateDir, file.Name()))
fileName := fmt.Sprintf("%s/%s", templateDir, file.Name())
content, err := templateFS.ReadFile(fileName)
return string(content), err
}
}

View File

@@ -37,8 +37,6 @@ import (
"k8s.io/client-go/rest"
"github.com/gosuri/uitable"
"github.com/olekukonko/tablewriter"
"github.com/pkg/errors"
"github.com/spf13/cobra"
types2 "k8s.io/apimachinery/pkg/types"
@@ -181,7 +179,7 @@ Enable addon for specific clusters, (local means control plane):
}
}
fmt.Printf("Addon: %s enabled Successfully.\n", name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name, false)
return nil
},
}
@@ -192,25 +190,21 @@ Enable addon for specific clusters, (local means control plane):
}
// AdditionalEndpointPrinter will print endpoints
func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient client.Client, name string) {
endpoints, _ := GetServiceEndpoints(ctx, k8sClient, pkgaddon.Convert2AppName(name), types.DefaultKubeVelaNS, c, Filter{})
if len(endpoints) > 0 {
table := tablewriter.NewWriter(os.Stdout)
table.SetColWidth(100)
table.SetHeader([]string{"Cluster", "Component", "Ref(Kind/Namespace/Name)", "Endpoint"})
for _, endpoint := range endpoints {
table.Append([]string{endpoint.Cluster, endpoint.Component, fmt.Sprintf("%s/%s/%s", endpoint.Ref.Kind, endpoint.Ref.Namespace, endpoint.Ref.Name), endpoint.String()})
}
fmt.Printf("Please access the %s from the following endpoints:\n", name)
table.Render()
func AdditionalEndpointPrinter(ctx context.Context, c common.Args, k8sClient client.Client, name string, isUpgrade bool) {
fmt.Printf("Please access the %s from the following endpoints:\n", name)
err := printAppEndpoints(ctx, k8sClient, pkgaddon.Convert2AppName(name), types.DefaultKubeVelaNS, Filter{}, c)
if err != nil {
fmt.Println("Get application endpoints error:", err)
return
}
if name == "velaux" {
fmt.Println(`To check the initialized admin user name and password by:`)
fmt.Println(` vela logs -n vela-system --name apiserver addon-velaux | grep "initialized admin username"`)
if !isUpgrade {
fmt.Println(`To check the initialized admin user name and password by:`)
fmt.Println(` vela logs -n vela-system --name apiserver addon-velaux | grep "initialized admin username"`)
}
fmt.Println(`To open the dashboard directly by port-forward:`)
fmt.Println(` vela port-forward -n vela-system addon-velaux 9082:80`)
fmt.Println(`Select "Cluster: local | Namespace: vela-system | Component: velaux | Kind: Service" from the prompt.`)
fmt.Println(`Select "Cluster: local | Namespace: vela-system | Kind: Service | Name: velaux" from the prompt.`)
fmt.Println(`Please refer to https://kubevela.io/docs/reference/addons/velaux for more VelaUX addon installation and visiting method.`)
}
}
@@ -284,7 +278,7 @@ Upgrade addon for specific clusters, (local means control plane):
}
fmt.Printf("Addon: %s\n enabled Successfully.", name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name)
AdditionalEndpointPrinter(ctx, c, k8sClient, name, true)
return nil
},
}
@@ -295,7 +289,7 @@ Upgrade addon for specific clusters, (local means control plane):
func parseAddonArgsToMap(args []string) (map[string]interface{}, error) {
res := map[string]interface{}{}
for _, arg := range args {
if err := strvals.ParseIntoString(arg, res); err != nil {
if err := strvals.ParseInto(arg, res); err != nil {
return nil, err
}
}

View File

@@ -70,6 +70,20 @@ func TestParseMap(t *testing.T) {
},
nilError: true,
},
{
args: []string{"local=true"},
res: map[string]interface{}{
"local": true,
},
nilError: true,
},
{
args: []string{"replicas=3"},
res: map[string]interface{}{
"replicas": int64(3),
},
nilError: true,
},
}
for _, s := range testcase {
r, err := parseAddonArgsToMap(s.args)

View File

@@ -27,27 +27,22 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
k8stypes "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
coreapi "github.com/oam-dev/kubevela/apis/core.oam.dev/common"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
"github.com/oam-dev/kubevela/pkg/definition"
"github.com/oam-dev/kubevela/pkg/oam"
"github.com/oam-dev/kubevela/pkg/utils/common"
"github.com/oam-dev/kubevela/pkg/utils/config"
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
"github.com/oam-dev/kubevela/references/plugins"
)
const (
// ProviderNamespace is the namespace of Terraform Cloud Provider
ProviderNamespace = "default"
providerNameParam = "name"
providerNameParam = "name"
errAuthenticateProvider = "failed to authenticate Terraform cloud provider %s"
)
// NewProviderCommand create `addon` command
@@ -193,38 +188,14 @@ func prepareProviderAddSubCommand(c common.Args, ioStreams cmdutil.IOStreams) ([
}
data, err := json.Marshal(properties)
if err != nil {
return fmt.Errorf("failed to authentiate Terraform cloud provier %s", providerType)
return fmt.Errorf(errAuthenticateProvider, providerType)
}
providerAppName := fmt.Sprintf("config-terraform-provider-%s", name)
a := &v1beta1.Application{}
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: providerAppName}, a); err != nil {
if kerrors.IsNotFound(err) {
a = &v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: providerAppName,
Namespace: types.DefaultKubeVelaNS,
},
Spec: v1beta1.ApplicationSpec{
Components: []coreapi.ApplicationComponent{
{
Name: providerAppName,
Type: providerType,
Properties: &runtime.RawExtension{
Raw: data,
},
},
},
},
}
if err := k8sClient.Create(ctx, a); err != nil {
return fmt.Errorf("failed to authentiate Terraform cloud provier %s", providerType)
}
ioStreams.Infof("Successfully authentiate provider %s for %s\n", name, providerType)
return nil
}
return fmt.Errorf("failed to authentiate Terraform cloud provier %s", providerType)
if err := config.CreateApplication(ctx, k8sClient, name, providerType, string(data), config.UIParam{}); err != nil {
return fmt.Errorf(errAuthenticateProvider, providerType)
}
return fmt.Errorf("terraform provider %s for %s already exists", name, providerType)
ioStreams.Infof("Successfully authenticate provider %s for %s\n", name, providerType)
return nil
}
cmds[i] = cmd
}
@@ -259,14 +230,14 @@ func listProviders(ctx context.Context, k8sClient client.Client, ioStreams cmdut
currentProviders []tcv1beta1.Provider
legacyProviders []tcv1beta1.Provider
)
tcProviders := &tcv1beta1.ProviderList{}
// client.MatchingLabels{: }
if err := k8sClient.List(ctx, tcProviders, client.InNamespace(ProviderNamespace)); err != nil {
l, err := config.ListTerraformProviders(ctx, k8sClient)
if err != nil {
return errors.Wrap(err, "failed to retrieve providers")
}
for _, p := range tcProviders.Items {
if p.Labels["config.oam.dev/type"] == types.TerraformProvider {
for _, p := range l {
// The first condition matches the providers created by `vela provider` in 1.3.2 and earlier.
if p.Labels[types.LabelConfigType] == types.TerraformProvider || p.Labels[types.LabelConfigCatalog] == types.VelaCoreConfig {
currentProviders = append(currentProviders, p)
} else {
// if not labeled, the provider is manually created or created by `vela addon enable`.
@@ -429,15 +400,8 @@ func prepareProviderDeleteSubCommand(c common.Args, ioStreams cmdutil.IOStreams)
if err != nil || name == "" {
return fmt.Errorf("must specify a name for the Terraform Cloud Provider %s", providerType)
}
providerAppName := fmt.Sprintf("config-terraform-provider-%s", name)
a := &v1beta1.Application{}
if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: types.DefaultKubeVelaNS, Name: providerAppName}, a); err != nil {
if kerrors.IsNotFound(err) {
return fmt.Errorf("provider %s for %s does not exist", name, providerType)
}
}
if err := k8sClient.Delete(ctx, a); err != nil {
return err
if err := config.DeleteApplication(ctx, k8sClient, name, true); err != nil {
return errors.Wrapf(err, "failed to delete Terraform Cloud Provider %s", name)
}
ioStreams.Infof("Successfully delete provider %s for %s\n", name, providerType)
return nil

View File

@@ -34,7 +34,7 @@ import (
"github.com/oam-dev/kubevela/pkg/utils/util"
)
func TestLlistProviders(t *testing.T) {
func TestListProviders(t *testing.T) {
ctx := context.Background()
type args struct {
k8sClient client.Client

View File

@@ -172,6 +172,9 @@ func printAppEndpoints(ctx context.Context, client client.Client, appName string
table.SetColWidth(100)
table.SetHeader([]string{"Cluster", "Component", "Ref(Kind/Namespace/Name)", "Endpoint"})
for _, endpoint := range endpoints {
if endpoint.Cluster == "" {
endpoint.Cluster = multicluster.ClusterLocalName
}
table.Append([]string{endpoint.Cluster, endpoint.Component, fmt.Sprintf("%s/%s/%s", endpoint.Ref.Kind, endpoint.Ref.Namespace, endpoint.Ref.Name), endpoint.String()})
}
table.Render()

View File

@@ -237,7 +237,7 @@ func PrintInstalledTraitDef(c common2.Args, io cmdutil.IOStreams, filter filterF
}
capa, err := ParseCapability(dm, data)
if err != nil {
io.Errorf("error parsing capability: %s\n", td.Name)
io.Errorf("error parsing capability: %s (message: %s)\n", td.Name, err.Error())
continue
}
if filter != nil && !filter(capa) {

View File

@@ -23,7 +23,6 @@ import (
"k8s.io/apimachinery/pkg/runtime"
apitypes "k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/util/retry"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -33,6 +32,7 @@ import (
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/apis/types"
velacmd "github.com/oam-dev/kubevela/pkg/cmd"
cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
"github.com/oam-dev/kubevela/pkg/component"
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/application"
"github.com/oam-dev/kubevela/pkg/controller/utils"

View File

@@ -17,92 +17,170 @@ limitations under the License.
package e2e_apiserver_test
import (
"encoding/json"
"fmt"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/pkg/errors"
"github.com/oam-dev/kubevela/pkg/addon"
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
)
var _ = Describe("Test addon rest api", func() {
registryName := "test-addon-registry"
createReq := apis.CreateAddonRegistryRequest{
Name: registryName,
Oss: &addon.OSSAddonSource{
Endpoint: "https://oss-cn-hangzhou.aliyuncs.com",
Bucket: "fake-kubevela-addons",
},
}
It("should add and delete a registry, list addons from default registry", func() {
defer GinkgoRecover()
By("add registry")
createRes := post("/addon_registries", createReq)
var rmeta apis.AddonRegistry
Expect(decodeResponseBody(createRes, &rmeta)).Should(Succeed())
Expect(rmeta.Name).Should(Equal(createReq.Name))
Expect(rmeta.Git).Should(Equal(createReq.Git))
Expect(rmeta.OSS).Should(Equal(createReq.Oss))
Describe("addon registry apiServer test", func() {
It("list addon registry", func() {
resp := get("/addon_registries")
defer resp.Body.Close()
var addonRegistry apisv1.ListAddonRegistryResponse
Expect(decodeResponseBody(resp, &addonRegistry)).Should(Succeed())
Expect(len(addonRegistry.Registries)).Should(BeEquivalentTo(1))
})
deleteRes := delete("/addon_registries/" + createReq.Name)
Expect(decodeResponseBody(deleteRes, nil)).Should(Succeed())
})
It("list addons", func() {
DefaultRegistry := "KubeVela"
listRes := get("/addons/")
var lres apis.ListAddonResponse
Expect(decodeResponseBody(listRes, &lres)).Should(Succeed())
Expect(lres.Addons).ShouldNot(BeZero())
Expect(lres.Addons[0].RegistryName).To(Equal(DefaultRegistry))
By("get addon detail")
detailRes := get("/addons/terraform-alibaba")
var dres apis.DetailAddonResponse
Expect(decodeResponseBody(detailRes, &dres)).Should(Succeed())
Expect(dres.Meta).ShouldNot(BeNil())
Expect(dres.UISchema).ShouldNot(BeNil())
Expect(dres.APISchema).ShouldNot(BeNil())
Expect(dres.RegistryName).Should(Equal(DefaultRegistry))
})
PIt("should enable and disable an addon", func() {
defer GinkgoRecover()
req := apis.EnableAddonRequest{
Args: map[string]interface{}{
"example": "test-args",
},
}
testAddon := "example"
res := post("/addons/"+testAddon+"/enable", req)
var statusRes apis.AddonStatusResponse
Expect(decodeResponseBody(res, &statusRes)).Should(Succeed())
Expect(statusRes.Phase).Should(Equal(apis.AddonPhaseEnabling))
// Wait for addon enabled
period := 30 * time.Second
timeout := 2 * time.Minute
Eventually(func() error {
res = get("/addons/" + testAddon + "/status")
defer res.Body.Close()
err := json.NewDecoder(res.Body).Decode(&statusRes)
Expect(err).Should(BeNil())
if statusRes.Phase == apis.AddonPhaseEnabled {
return nil
It("add addon registry", func() {
req := apisv1.CreateAddonRegistryRequest{
Name: "test-registry",
Git: &addon.GitAddonSource{
URL: "github.com/test-path",
},
}
return errors.New("not ready")
}, timeout, period).Should(BeNil())
res := post("/addon_registries", req)
defer res.Body.Close()
var registry apisv1.AddonRegistry
Expect(decodeResponseBody(res, &registry)).Should(Succeed())
Expect(registry.Git).ShouldNot(BeNil())
Expect(registry.Git.URL).Should(BeEquivalentTo("github.com/test-path"))
res = post("/addons/"+testAddon+"/disable", req)
Expect(decodeResponseBody(res, &statusRes)).Should(Succeed())
resp := get("/addon_registries")
var addonRegistry apisv1.ListAddonRegistryResponse
Expect(decodeResponseBody(resp, &addonRegistry)).Should(Succeed())
Expect(len(addonRegistry.Registries)).Should(BeEquivalentTo(2))
})
It("update an addon registry", func() {
req := apisv1.UpdateAddonRegistryRequest{
Git: &addon.GitAddonSource{
URL: "github.com/another-path",
},
}
res := put("/addon_registries"+"/test-registry", req)
defer res.Body.Close()
var registry apisv1.AddonRegistry
Expect(decodeResponseBody(res, &registry)).Should(Succeed())
Expect(registry.Git).ShouldNot(BeNil())
Expect(registry.Git.URL).Should(BeEquivalentTo("github.com/another-path"))
resp := get("/addon_registries")
var addonRegistry apisv1.ListAddonRegistryResponse
Expect(decodeResponseBody(resp, &addonRegistry)).Should(Succeed())
Expect(len(addonRegistry.Registries)).Should(BeEquivalentTo(2))
Expect(addonRegistry.Registries[1].Git.URL).Should(BeEquivalentTo("github.com/another-path"))
})
It("delete an addon registry", func() {
res := delete("/addon_registries" + "/test-registry")
defer res.Body.Close()
var registry apisv1.AddonRegistry
Expect(decodeResponseBody(res, &registry)).Should(Succeed())
})
})
It("should delete test registry", func() {
defer GinkgoRecover()
Describe("addon apiServer test", func() {
It("list addons", func() {
res := get("/addons")
defer res.Body.Close()
var addons apisv1.ListAddonResponse
Expect(decodeResponseBody(res, &addons)).Should(Succeed())
Expect(len(addons.Addons)).ShouldNot(BeEquivalentTo(0))
})
It("get addon detail", func() {
res := get("/addons/mock-addon")
defer res.Body.Close()
var addon apisv1.DetailAddonResponse
Expect(decodeResponseBody(res, &addon)).Should(Succeed())
Expect(addon.Name).Should(BeEquivalentTo("mock-addon"))
})
It("enable addon ", func() {
req := apisv1.EnableAddonRequest{
Args: map[string]interface{}{
"testkey": "testvalue",
},
}
res := post("/addons/mock-addon/enable", req)
defer res.Body.Close()
var addon apisv1.AddonStatusResponse
Expect(decodeResponseBody(res, &addon)).Should(Succeed())
Expect(addon.Name).Should(BeEquivalentTo("mock-addon"))
Expect(len(addon.Args)).Should(BeEquivalentTo(1))
Expect(addon.Args["testkey"]).Should(BeEquivalentTo("testvalue"))
})
It("addon status", func() {
res := get("/addons/mock-addon/status")
defer res.Body.Close()
var addonStatus apisv1.AddonStatusResponse
Expect(decodeResponseBody(res, &addonStatus)).Should(Succeed())
Expect(addonStatus.Name).Should(BeEquivalentTo("mock-addon"))
Expect(len(addonStatus.Args)).Should(BeEquivalentTo(1))
Expect(addonStatus.Args["testkey"]).Should(BeEquivalentTo("testvalue"))
})
It("not enabled addon status", func() {
res := get("/addons/example/status")
defer res.Body.Close()
var addonStatus apisv1.AddonStatusResponse
Expect(decodeResponseBody(res, &addonStatus)).Should(Succeed())
Expect(addonStatus.Name).Should(BeEquivalentTo("example"))
Expect(addonStatus.Phase).Should(BeEquivalentTo("disabled"))
})
It("update addon ", func() {
req := apisv1.EnableAddonRequest{
Args: map[string]interface{}{
"testkey": "new-testvalue",
},
}
res := put("/addons/mock-addon/update", req)
defer res.Body.Close()
var addonStatus apisv1.AddonStatusResponse
Expect(decodeResponseBody(res, &addonStatus)).Should(Succeed())
Expect(addonStatus.Name).Should(BeEquivalentTo("mock-addon"))
Expect(len(addonStatus.Args)).Should(BeEquivalentTo(1))
Expect(addonStatus.Args["testkey"]).Should(BeEquivalentTo("new-testvalue"))
status := get("/addons/mock-addon/status")
var newaddonStatus apisv1.AddonStatusResponse
Expect(decodeResponseBody(status, &newaddonStatus)).Should(Succeed())
Expect(newaddonStatus.Name).Should(BeEquivalentTo("mock-addon"))
Expect(len(newaddonStatus.Args)).Should(BeEquivalentTo(1))
Expect(newaddonStatus.Args["testkey"]).Should(BeEquivalentTo("new-testvalue"))
})
It("list enabled addon", func() {
Eventually(func() error {
res := get("/enabled_addon/")
defer res.Body.Close()
var addonList apisv1.ListEnabledAddonResponse
err := decodeResponseBody(res, &addonList)
if err != nil {
return err
}
if len(addonList.EnabledAddons) == 0 {
return fmt.Errorf("error number")
}
return nil
}, 30*time.Second, 300*time.Millisecond).Should(BeNil())
})
It("disable addon ", func() {
res := post("/addons/mock-addon/disable", nil)
defer res.Body.Close()
var addonStatus apisv1.AddonStatusResponse
Expect(decodeResponseBody(res, &addonStatus)).Should(Succeed())
Expect(addonStatus.Name).Should(BeEquivalentTo("mock-addon"))
})
})
})

View File

@@ -200,7 +200,6 @@ func delete(path string) *http.Response {
req.Header.Add("Authorization", token)
response, err := client.Do(req)
Expect(err).Should(BeNil())
defer response.Body.Close()
return response
}

View File

@@ -29,16 +29,16 @@ var _ = Describe("Test system info rest api", func() {
res := get("/system_info/")
var info apisv1.SystemInfoResponse
Expect(decodeResponseBody(res, &info)).Should(Succeed())
Expect(len(info.InstallID)).ShouldNot(BeEquivalentTo(0))
Expect(len(info.PlatformID)).ShouldNot(BeEquivalentTo(0))
Expect(info.EnableCollection).Should(BeEquivalentTo(true))
systemID := info.InstallID
systemID := info.PlatformID
// check several times the systemID should not change
for i := 0; i < 5; i++ {
res := get("/system_info/")
var checkInfo apisv1.SystemInfoResponse
Expect(decodeResponseBody(res, &checkInfo)).Should(Succeed())
Expect(checkInfo.InstallID).Should(BeEquivalentTo(systemID))
Expect(checkInfo.PlatformID).Should(BeEquivalentTo(systemID))
}
})
@@ -46,33 +46,33 @@ var _ = Describe("Test system info rest api", func() {
res := get("/system_info/")
var info apisv1.SystemInfoResponse
Expect(decodeResponseBody(res, &info)).Should(Succeed())
Expect(len(info.InstallID)).ShouldNot(BeEquivalentTo(0))
Expect(len(info.PlatformID)).ShouldNot(BeEquivalentTo(0))
Expect(info.EnableCollection).Should(BeEquivalentTo(true))
installID := info.InstallID
installID := info.PlatformID
res = put("/system_info/", apisv1.SystemInfoRequest{EnableCollection: false})
info = apisv1.SystemInfoResponse{}
Expect(decodeResponseBody(res, &info)).Should(Succeed())
Expect(len(info.InstallID)).ShouldNot(BeEquivalentTo(0))
Expect(len(info.PlatformID)).ShouldNot(BeEquivalentTo(0))
Expect(info.EnableCollection).Should(BeEquivalentTo(false))
res = get("/system_info/")
var checkInfo apisv1.SystemInfoResponse
Expect(decodeResponseBody(res, &checkInfo)).Should(Succeed())
Expect(checkInfo.InstallID).Should(BeEquivalentTo(installID))
Expect(checkInfo.PlatformID).Should(BeEquivalentTo(installID))
Expect(checkInfo.EnableCollection).Should(BeEquivalentTo(false))
res = put("/system_info/", apisv1.SystemInfoRequest{EnableCollection: true})
var enableInfo apisv1.SystemInfoResponse
Expect(decodeResponseBody(res, &enableInfo)).Should(Succeed())
Expect(len(enableInfo.InstallID)).ShouldNot(BeEquivalentTo(0))
Expect(len(enableInfo.PlatformID)).ShouldNot(BeEquivalentTo(0))
Expect(enableInfo.EnableCollection).Should(BeEquivalentTo(true))
Expect(enableInfo.InstallID).Should(BeEquivalentTo(installID))
Expect(enableInfo.PlatformID).Should(BeEquivalentTo(installID))
res = get("/system_info/")
var checkAgainInfo apisv1.SystemInfoResponse
Expect(decodeResponseBody(res, &checkAgainInfo)).Should(Succeed())
Expect(checkAgainInfo.InstallID).Should(BeEquivalentTo(installID))
Expect(checkAgainInfo.PlatformID).Should(BeEquivalentTo(installID))
Expect(checkAgainInfo.EnableCollection).Should(BeEquivalentTo(true))
})
})

View File

@@ -203,7 +203,7 @@ var _ = Describe("Test velaQL rest api", func() {
}, 2*time.Minute, 3*time.Microsecond).Should(BeNil())
})
PIt("Test collect pod from helmRelease", func() {
It("Test collect pod from helmRelease", func() {
appWithHelm := new(v1beta1.Application)
Expect(yaml.Unmarshal([]byte(podInfoApp), appWithHelm)).Should(BeNil())
req := apiv1.ApplicationRequest{

View File

@@ -12,6 +12,7 @@ import (
"catalog.config.oam.dev": "velacore-config"
"type.config.oam.dev": "image-registry"
"multi-cluster.config.oam.dev": "true"
"ui-hidden": "true"
}
description: "Config information to authenticate image registry"
attributes: workload: type: "autodetects.core.oam.dev"
@@ -32,9 +33,14 @@ template: {
"config.oam.dev/sub-type": "auth"
}
}
type: "kubernetes.io/dockerconfigjson"
stringData: {
if parameter.auth != _|_ {
if parameter.auth != _|_ {
type: "kubernetes.io/dockerconfigjson"
}
if parameter.auth == _|_ {
type: "Opaque"
}
if parameter.auth != _|_ {
stringData: {
".dockerconfigjson": json.Marshal({
"auths": "\(parameter.registry)": {
"username": parameter.auth.username

View File

@@ -175,10 +175,9 @@ template: {
parameter.labels
}
if parameter.addRevisionLabel {
"app.oam.dev/appRevision": context.appRevision
"app.oam.dev/revision": context.revision
}
"app.oam.dev/component": context.name
"app.oam.dev/revision": context.revision
}
if parameter.annotations != _|_ {
annotations: parameter.annotations
@@ -379,7 +378,7 @@ template: {
exposeType: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName"
// +ignore
// +usage=If addRevisionLabel is true, the appRevision label will be added to the underlying pods
// +usage=If addRevisionLabel is true, the revision label will be added to the underlying pods
addRevisionLabel: *false | bool
// +usage=Commands to run in the container
@@ -501,7 +500,7 @@ template: {
readinessProbe?: #HealthProbe
// +usage=Specify the hostAliases to add
hostAliases: [...{
hostAliases?: [...{
ip: string
hostnames: [...string]
}]

View File

@@ -39,7 +39,7 @@ template: {
}]
}
if _baseEnv != _|_ {
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar.value}}
_baseEnvMap: {for envVar in _baseEnv {"\(envVar.name)": envVar}}
// +patchStrategy=replace
env: [ for envVar in _baseEnv if _delKeys[envVar.name] == _|_ && !_params.replace {
name: envVar.name
@@ -47,11 +47,15 @@ template: {
value: _params.env[envVar.name]
}
if _params.env[envVar.name] == _|_ {
value: envVar.value
if envVar.value != _|_ {
value: envVar.value
}
if envVar.valueFrom != _|_ {
valueFrom: envVar.valueFrom
}
}
}] + [ for k, v in _params.env if _delKeys[k] == _|_ && (_params.replace || _baseEnvMap[k] == _|_) {
name: k
value: v
v
}]
}
}