Compare commits

..

7 Commits

Author SHA1 Message Date
github-actions[bot]
a783393ebd [Backport release-1.6] Fix: bug of filter registry func will modify origin data (#5120)
* fix filter registry func flaky

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

* fix comments

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-11-24 15:06:29 +08:00
github-actions[bot]
a19ed0b510 [Backport release-1.6] Chore: add definition example doc CI check (#5119)
* Chore: add definition example doc CI check

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 1afaaf047d)

* Fix: add example doc for trait

Signed-off-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
(cherry picked from commit 6c61643b8a)

Co-authored-by: Jianbo Sun <jianbo.sjb@alibaba-inc.com>
2022-11-24 15:05:18 +08:00
github-actions[bot]
03223aa786 [Backport release-1.6] Fix: bug when addon dependent an addon in other registry (#5115)
* fix several bugs of addon

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

* fix golint error

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

* fix error and add tests

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

* fix comments and fix apiserver test

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

* fix typo

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

* fix tests

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

* small fix

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

* small fix

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

* add parameter in apiserver and test

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

Co-authored-by: 楚岳 <wangyike.wyk@alibaba-inc.com>
2022-11-24 09:46:54 +08:00
github-actions[bot]
55c8dad116 Fix: multicluster cluster scope ref (#5112)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 40a9a981d9)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-11-24 09:16:15 +08:00
github-actions[bot]
38c57c38c8 [Backport release-1.6] Fix: end test environments (#5107)
* Fix: end test environments

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 9d573fc6a0)

* fix

Signed-off-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
(cherry picked from commit 81deb6a588)

Co-authored-by: Qiaozp <qiaozhongpei.qzp@alibaba-inc.com>
2022-11-22 19:37:12 +08:00
github-actions[bot]
7f734e9479 Fix: patchOutputs bug for multiple outputs (#5104)
Signed-off-by: Somefive <yd219913@alibaba-inc.com>
(cherry picked from commit 4864ca3a27)

Co-authored-by: Somefive <yd219913@alibaba-inc.com>
2022-11-22 11:40:20 +08:00
github-actions[bot]
7814232b7c Fix: fix acr webhook for enterprise registry (#5098)
Signed-off-by: FogDong <dongtianxin.tx@alibaba-inc.com>
(cherry picked from commit c0519d3fba)

Co-authored-by: FogDong <dongtianxin.tx@alibaba-inc.com>
2022-11-21 14:21:58 +08:00
58 changed files with 1119 additions and 105 deletions

44
.github/workflows/definition-lint.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: Definition-Lint
on:
push:
branches:
- master
- release-*
workflow_dispatch: {}
pull_request:
branches:
- master
- release-*
env:
# Common versions
GO_VERSION: '1.19'
jobs:
definition-doc:
runs-on: ubuntu-latest
steps:
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: ${{ env.GO_VERSION }}
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
- name: Setup K3d
uses: nolar/setup-k3d-k3s@v1.0.9
with:
version: v1.20
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Definition Doc generate check
run: |
go build -o docgen hack/docgen/def/gen.go
./docgen --type=comp --force-example-doc --path=./comp-def-check.md
./docgen --type=trait --force-example-doc --path=./trait-def-check.md
./docgen --type=wf --force-example-doc --path=./wf-def-check.md
./docgen --type=policy --force-example-doc --path=./policy-def-check.md

View File

@@ -0,0 +1,8 @@
name: mock-dep-addon
version: v1.0.0
description: Vela test addon named mock-dep-addon
icon: https://www.test.com/icon
url: https://www.test.com
dependencies:
- name: mock-be-dep-addon

View File

@@ -60,4 +60,13 @@ entries:
urls:
- http://127.0.0.1:9098/helm/bar-v2.0.0.tgz
version: v2.0.0
mock-be-dep-addon:
- created: "2022-10-29T09:11:16.865230605Z"
description: Vela test addon named mock-be-dep-addon
home: https://www.test.com/icon
icon: https://www.test.com
name: mock-be-dep-addon
urls:
- http://127.0.0.1:9098/helm/mock-be-dep-addon-v1.0.0.tgz
version: v1.0.0
generated: "2022-06-15T13:17:04.733573+08:00"

View File

@@ -149,6 +149,12 @@ var helmHandler http.HandlerFunc = func(rw http.ResponseWriter, req *http.Reques
_, _ = rw.Write([]byte(err.Error()))
}
rw.Write(file)
case strings.Contains(req.URL.Path, "mock-be-dep-addon-v1.0.0.tgz"):
file, err := os.ReadFile("./e2e/addon/mock/testrepo/helm-repo/mock-be-dep-addon-v1.0.0.tgz")
if err != nil {
_, _ = rw.Write([]byte(err.Error()))
}
rw.Write(file)
}
}

View File

@@ -42,6 +42,7 @@ func main() {
defdir := flag.String("def-dir", "", "path of definition dir")
tp := flag.String("type", "", "choose one of the definition to print")
i18nfile := flag.String("i18n", "../kubevela.io/static/reference-i18n.json", "file path of i18n data")
forceExample := flag.Bool("force-example-doc", false, "example must be provided for definitions")
flag.Parse()
if *i18nfile != "" {
@@ -52,21 +53,29 @@ func main() {
fmt.Println("you must specify a type with definition ref path specified ")
os.Exit(1)
}
opt := mods.Options{
Path: *path,
Location: *location,
DefDir: *defdir,
ForceExamples: *forceExample,
}
fmt.Printf("creating docs with args path=%s, location=%s, defdir=%s, type=%s.\n", *path, *location, *defdir, *tp)
switch types.CapType(*tp) {
case types.TypeComponentDefinition, "component", "comp":
mods.ComponentDef(ctx, c, path, location, *defdir)
mods.ComponentDef(ctx, c, opt)
case types.TypeTrait:
mods.TraitDef(ctx, c, path, location, *defdir)
mods.TraitDef(ctx, c, opt)
case types.TypePolicy:
mods.PolicyDef(ctx, c, path, location, *defdir)
mods.PolicyDef(ctx, c, opt)
case types.TypeWorkflowStep, "workflow", "wf":
mods.WorkflowDef(ctx, c, path, location, *defdir)
mods.WorkflowDef(ctx, c, opt)
case "":
mods.ComponentDef(ctx, c, path, location, *defdir)
mods.TraitDef(ctx, c, path, location, *defdir)
mods.PolicyDef(ctx, c, path, location, *defdir)
mods.WorkflowDef(ctx, c, path, location, *defdir)
mods.ComponentDef(ctx, c, opt)
mods.TraitDef(ctx, c, opt)
mods.PolicyDef(ctx, c, opt)
mods.WorkflowDef(ctx, c, opt)
default:
fmt.Printf("type %s not supported\n", *tp)
os.Exit(1)

View File

@@ -19,7 +19,6 @@ package mods
import (
"context"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
@@ -60,12 +59,13 @@ title: 内置组件列表
` + fmt.Sprintf("> 本文档由[脚本](../../contributor/cli-ref-doc)自动生成,请勿手动修改,上次更新于 %s。\n\n", time.Now().Format(time.RFC3339))
// ComponentDef generate component def reference doc
func ComponentDef(ctx context.Context, c common.Args, path, location *string, defdir string) {
if defdir == "" {
defdir = ComponentDefDir
func ComponentDef(ctx context.Context, c common.Args, opt Options) {
if opt.DefDir == "" {
opt.DefDir = ComponentDefDir
}
ref := &docgen.MarkdownReference{
AllInOne: true,
AllInOne: true,
ForceExample: opt.ForceExamples,
Filter: func(capability types.Capability) bool {
if capability.Type != types.TypeComponentDefinition || capability.Category != types.CUECategory {
return false
@@ -74,9 +74,9 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
return false
}
// only print capability which contained in cue def
files, err := ioutil.ReadDir(defdir)
files, err := os.ReadDir(opt.DefDir)
if err != nil {
fmt.Println("read dir err", defdir, err)
fmt.Println("read dir err", opt.DefDir, err)
return false
}
for _, f := range files {
@@ -96,19 +96,20 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
return
}
ref.DiscoveryMapper = dm
if *path != "" {
if opt.Path != "" {
ref.I18N = &docgen.En
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
if strings.Contains(opt.Location, "zh") || strings.Contains(opt.Location, "chinese") {
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomComponentHeaderZH
}
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
if err := ref.GenerateReferenceDocs(ctx, c, opt.Path); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), opt.Path)
return
}
if *location == "" || *location == "en" {
if opt.Location == "" || opt.Location == "en" {
ref.I18N = &docgen.En
if err := ref.GenerateReferenceDocs(ctx, c, ComponentDefRefPath); err != nil {
fmt.Println(err)
@@ -116,7 +117,7 @@ func ComponentDef(ctx context.Context, c common.Args, path, location *string, de
}
fmt.Printf("component reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), ComponentDefRefPath)
}
if *location == "" || *location == "zh" {
if opt.Location == "" || opt.Location == "zh" {
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomComponentHeaderZH
if err := ref.GenerateReferenceDocs(ctx, c, ComponentDefRefPathZh); err != nil {

View File

@@ -19,7 +19,6 @@ package mods
import (
"context"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
@@ -58,12 +57,13 @@ title: 内置策略列表
` + fmt.Sprintf("> 本文档由[脚本](../../contributor/cli-ref-doc)自动生成,请勿手动修改,上次更新于 %s。\n\n", time.Now().Format(time.RFC3339))
// PolicyDef generate policy def reference doc
func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdir string) {
if defdir == "" {
defdir = PolicyDefDir
func PolicyDef(ctx context.Context, c common.Args, opt Options) {
if opt.DefDir == "" {
opt.DefDir = PolicyDefDir
}
ref := &docgen.MarkdownReference{
AllInOne: true,
AllInOne: true,
ForceExample: opt.ForceExamples,
Filter: func(capability types.Capability) bool {
if capability.Type != types.TypePolicy || capability.Category != types.CUECategory {
return false
@@ -72,9 +72,9 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
return false
}
// only print capability which contained in cue def
files, err := ioutil.ReadDir(defdir)
files, err := os.ReadDir(opt.DefDir)
if err != nil {
fmt.Println("read dir err", defdir, err)
fmt.Println("read dir err", opt.DefDir, err)
return false
}
for _, f := range files {
@@ -86,20 +86,21 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
},
CustomDocHeader: CustomPolicyHeaderEN,
}
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
if *path != "" {
ref.Local = &docgen.FromLocal{Path: PolicyDefDir}
if opt.Path != "" {
ref.I18N = &docgen.En
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
if strings.Contains(opt.Location, "zh") || strings.Contains(opt.Location, "chinese") {
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomPolicyHeaderZH
}
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
if err := ref.GenerateReferenceDocs(ctx, c, opt.Path); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), opt.Path)
return
}
if *location == "" || *location == "en" {
if opt.Location == "" || opt.Location == "en" {
ref.I18N = &docgen.En
if err := ref.GenerateReferenceDocs(ctx, c, PolicyDefRefPath); err != nil {
fmt.Println(err)
@@ -107,7 +108,7 @@ func PolicyDef(ctx context.Context, c common.Args, path, location *string, defdi
}
fmt.Printf("policy reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), PolicyDefRefPath)
}
if *location == "" || *location == "zh" {
if opt.Location == "" || opt.Location == "zh" {
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomPolicyHeaderZH
if err := ref.GenerateReferenceDocs(ctx, c, PolicyDefRefPathZh); err != nil {

View File

@@ -19,7 +19,6 @@ package mods
import (
"context"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
@@ -58,12 +57,13 @@ title: 内置运维特征列表
` + fmt.Sprintf("> 本文档由[脚本](../../contributor/cli-ref-doc)自动生成,请勿手动修改,上次更新于 %s。\n\n", time.Now().Format(time.RFC3339))
// TraitDef generate trait def reference doc
func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir string) {
if defdir == "" {
defdir = TraitDefDir
func TraitDef(ctx context.Context, c common.Args, opt Options) {
if opt.DefDir == "" {
opt.DefDir = TraitDefDir
}
ref := &docgen.MarkdownReference{
AllInOne: true,
AllInOne: true,
ForceExample: opt.ForceExamples,
Filter: func(capability types.Capability) bool {
if capability.Type != types.TypeTrait || capability.Category != types.CUECategory {
return false
@@ -72,9 +72,9 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
return false
}
// only print capability which contained in cue def
files, err := ioutil.ReadDir(defdir)
files, err := os.ReadDir(opt.DefDir)
if err != nil {
fmt.Println("read dir err", defdir, err)
fmt.Println("read dir err", opt.DefDir, err)
return false
}
for _, f := range files {
@@ -90,20 +90,20 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
Path: TraitDefDir,
}
if *path != "" {
if opt.Path != "" {
ref.I18N = &docgen.En
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
if strings.Contains(opt.Location, "zh") || strings.Contains(opt.Location, "chinese") {
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomTraitHeaderZH
}
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
if err := ref.GenerateReferenceDocs(ctx, c, opt.Path); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), opt.Path)
} else {
// Generate to default path depends on language
if *location == "" || *location == "en" {
if opt.Location == "" || opt.Location == "en" {
ref.I18N = &docgen.En
if err := ref.GenerateReferenceDocs(ctx, c, TraitDefRefPath); err != nil {
fmt.Println(err)
@@ -111,7 +111,7 @@ func TraitDef(ctx context.Context, c common.Args, path, location *string, defdir
}
fmt.Printf("trait reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), TraitDefRefPath)
}
if *location == "" || *location == "zh" {
if opt.Location == "" || opt.Location == "zh" {
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomTraitHeaderZH
if err := ref.GenerateReferenceDocs(ctx, c, TraitDefRefPathZh); err != nil {

View File

@@ -0,0 +1,25 @@
/*
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 mods
// Options defines the doc generate options
type Options struct {
Path string
Location string
DefDir string
ForceExamples bool
}

View File

@@ -19,7 +19,6 @@ package mods
import (
"context"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
@@ -58,12 +57,13 @@ title: 内置工作流步骤列表
` + fmt.Sprintf("> 本文档由[脚本](../../contributor/cli-ref-doc)自动生成,请勿手动修改,上次更新于 %s。\n\n", time.Now().Format(time.RFC3339))
// WorkflowDef generate workflow def reference doc
func WorkflowDef(ctx context.Context, c common.Args, path, location *string, defdir string) {
if defdir == "" {
defdir = WorkflowDefDir
func WorkflowDef(ctx context.Context, c common.Args, opt Options) {
if opt.DefDir == "" {
opt.DefDir = WorkflowDefDir
}
ref := &docgen.MarkdownReference{
AllInOne: true,
AllInOne: true,
ForceExample: opt.ForceExamples,
Filter: func(capability types.Capability) bool {
if capability.Type != types.TypeWorkflowStep || capability.Category != types.CUECategory {
@@ -74,9 +74,9 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
return false
}
// only print capability which contained in cue def
files, err := ioutil.ReadDir(defdir)
files, err := os.ReadDir(opt.DefDir)
if err != nil {
fmt.Println("read dir err", defdir, err)
fmt.Println("read dir err", opt.DefDir, err)
return false
}
for _, f := range files {
@@ -88,21 +88,22 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
},
CustomDocHeader: CustomWorkflowHeaderEN,
}
ref.Remote = &docgen.FromCluster{Namespace: types.DefaultKubeVelaNS}
ref.Local = &docgen.FromLocal{Path: WorkflowDefDir}
if *path != "" {
if opt.Path != "" {
ref.I18N = &docgen.En
if strings.Contains(*location, "zh") || strings.Contains(*location, "chinese") {
if strings.Contains(opt.Location, "zh") || strings.Contains(opt.Location, "chinese") {
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomWorkflowHeaderZH
}
if err := ref.GenerateReferenceDocs(ctx, c, *path); err != nil {
if err := ref.GenerateReferenceDocs(ctx, c, opt.Path); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), *path)
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), opt.Path)
return
}
if *location == "" || *location == "en" {
if opt.Location == "" || opt.Location == "en" {
ref.I18N = &docgen.En
if err := ref.GenerateReferenceDocs(ctx, c, WorkflowDefRefPath); err != nil {
fmt.Println(err)
@@ -110,7 +111,7 @@ func WorkflowDef(ctx context.Context, c common.Args, path, location *string, def
}
fmt.Printf("workflow reference docs (%s) successfully generated in %s \n", ref.I18N.Language(), WorkflowDefRefPath)
}
if *location == "" || *location == "zh" {
if opt.Location == "" || opt.Location == "zh" {
ref.I18N = &docgen.Zh
ref.CustomDocHeader = CustomWorkflowHeaderZH
if err := ref.GenerateReferenceDocs(ctx, c, WorkflowDefRefPathZh); err != nil {

View File

@@ -932,10 +932,12 @@ type Installer struct {
dryRun bool
dryRunBuff *bytes.Buffer
registries []Registry
}
// NewAddonInstaller will create an installer for addon
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache, opts ...InstallOption) Installer {
func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r *Registry, args map[string]interface{}, cache *Cache, registries []Registry, opts ...InstallOption) Installer {
i := Installer{
ctx: ctx,
config: config,
@@ -946,6 +948,7 @@ func NewAddonInstaller(ctx context.Context, cli client.Client, discoveryClient *
cache: cache,
dc: discoveryClient,
dryRunBuff: &bytes.Buffer{},
registries: registries,
}
for _, opt := range opts {
opt(&i)
@@ -1042,16 +1045,41 @@ func (h *Installer) installDependency(addon *InstallPackage) error {
if h.dryRun {
continue
}
// always install addon's latest version
depAddon, err := h.loadInstallPackage(dep.Name, dep.Version)
if err != nil {
return err
}
depHandler := *h
depHandler.args = nil
if err = depHandler.enableAddon(depAddon); err != nil {
return errors.Wrap(err, "fail to dispatch dependent addon resource")
var depAddon *InstallPackage
// try to install the dependent addon from the same registry with the current addon
depAddon, err = h.loadInstallPackage(dep.Name, dep.Version)
if err == nil {
if err = depHandler.enableAddon(depAddon); err != nil {
return errors.Wrap(err, "fail to dispatch dependent addon resource")
}
return nil
}
if !errors.Is(err, ErrNotExist) {
return err
}
for _, registry := range h.registries {
// try to install dependent addon from other registries
depHandler.r = &Registry{
Name: registry.Name, Helm: registry.Helm, OSS: registry.OSS, Git: registry.Git, Gitee: registry.Gitee, Gitlab: registry.Gitlab,
}
depAddon, err = depHandler.loadInstallPackage(dep.Name, dep.Version)
if err == nil {
break
}
if errors.Is(err, ErrNotExist) {
continue
}
return err
}
if err == nil {
if err = depHandler.enableAddon(depAddon); err != nil {
return errors.Wrap(err, "fail to dispatch dependent addon resource")
}
return nil
}
return fmt.Errorf("dependency addon: %s with version: %s cannot be found from all registries", dep.Name, dep.Version)
}
if h.dryRun && len(dependencies) > 0 {
klog.Warningf("dry run addon won't install dependencies, please make sure your system has already installed these addons: %v", strings.Join(dependencies, ", "))

View File

@@ -355,7 +355,7 @@ var _ = Describe("func addon update ", func() {
}, time.Millisecond*500, 30*time.Second).Should(BeNil())
pkg := &InstallPackage{Meta: Meta{Name: "test-update", Version: "1.3.0"}}
h := NewAddonInstaller(context.Background(), k8sClient, nil, nil, nil, &Registry{Name: "test"}, nil, nil)
h := NewAddonInstaller(context.Background(), k8sClient, nil, nil, nil, &Registry{Name: "test"}, nil, nil, nil)
h.addon = pkg
Expect(h.dispatchAddonResource(pkg)).Should(BeNil())
@@ -418,7 +418,7 @@ var _ = Describe("test dry-run addon from local dir", func() {
pkg, err := GetInstallPackageFromReader(r, &meta, UIData)
Expect(err).Should(BeNil())
h := NewAddonInstaller(ctx, k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, &Registry{Name: LocalAddonRegistryName}, map[string]interface{}{"example": "test-dry-run"}, nil, DryRunAddon)
h := NewAddonInstaller(ctx, k8sClient, dc, apply.NewAPIApplicator(k8sClient), cfg, &Registry{Name: LocalAddonRegistryName}, map[string]interface{}{"example": "test-dry-run"}, nil, nil, DryRunAddon)
err = h.enableAddon(pkg)
Expect(err).Should(BeNil())

View File

@@ -55,8 +55,8 @@ const (
)
// EnableAddon will enable addon with dependency check, source is where addon from.
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache, opts ...InstallOption) error {
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache, opts...)
func EnableAddon(ctx context.Context, name string, version string, cli client.Client, discoveryClient *discovery.DiscoveryClient, apply apply.Applicator, config *rest.Config, r Registry, args map[string]interface{}, cache *Cache, registries []Registry, opts ...InstallOption) error {
h := NewAddonInstaller(ctx, cli, discoveryClient, apply, config, &r, args, cache, registries, opts...)
pkg, err := h.loadInstallPackage(name, version)
if err != nil {
return err
@@ -113,7 +113,7 @@ func EnableAddonByLocalDir(ctx context.Context, name string, dir string, cli cli
if err != nil {
return err
}
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil, opts...)
h := NewAddonInstaller(ctx, cli, dc, applicator, config, &Registry{Name: LocalAddonRegistryName}, args, nil, nil, opts...)
needEnableAddonNames, err := h.checkDependency(pkg)
if err != nil {
return err

View File

@@ -187,7 +187,7 @@ func findLegacyAddonDefs(ctx context.Context, k8sClient client.Client, addonName
if registry.Name == registryName {
var uiData *UIData
if !IsVersionRegistry(registry) {
installer := NewAddonInstaller(ctx, k8sClient, nil, nil, config, &registries[i], nil, nil)
installer := NewAddonInstaller(ctx, k8sClient, nil, nil, config, &registries[i], nil, nil, nil)
metas, err := installer.getAddonMeta()
if err != nil {
return err
@@ -502,3 +502,17 @@ func checkBondComponentExist(u unstructured.Unstructured, app v1beta1.Applicatio
}
return false
}
// FilterDependencyRegistries will return all registries besides the target registry itself
func FilterDependencyRegistries(i int, rs []Registry) []Registry {
if i >= len(rs) {
return rs
}
if i < 0 {
return rs
}
ret := make([]Registry, len(rs)-1)
copy(ret, rs[:i])
copy(ret[i:], rs[i+1:])
return ret
}

View File

@@ -329,6 +329,57 @@ func TestCheckObjectBindingComponent(t *testing.T) {
}
}
func TestFilterDependencyRegistries(t *testing.T) {
testCases := []struct {
registries []Registry
index int
res []Registry
origin []Registry
}{
{
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
index: 0,
res: []Registry{{Name: "r2"}, {Name: "r3"}},
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
},
{
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
index: 1,
res: []Registry{{Name: "r1"}, {Name: "r3"}},
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
},
{
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
index: 2,
res: []Registry{{Name: "r1"}, {Name: "r2"}},
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
},
{
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
index: 3,
res: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
},
{
registries: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
index: -1,
res: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
origin: []Registry{{Name: "r1"}, {Name: "r2"}, {Name: "r3"}},
},
{
registries: []Registry{},
index: 0,
res: []Registry{},
origin: []Registry{},
},
}
for _, testCase := range testCases {
res := FilterDependencyRegistries(testCase.index, testCase.registries)
assert.Equal(t, res, testCase.res)
assert.Equal(t, testCase.registries, testCase.origin)
}
}
const (
compDefYaml = `
apiVersion: core.oam.dev/v1beta1

View File

@@ -386,6 +386,7 @@ type ApplicationTrigger struct {
Type string `json:"type"`
PayloadType string `json:"payloadType"`
ComponentName string `json:"componentName"`
Registry string `json:"registry,omitempty"`
}
const (

View File

@@ -400,8 +400,22 @@ func (u *addonServiceImpl) EnableAddon(ctx context.Context, name string, args ap
if err != nil {
return err
}
for _, r := range registries {
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache)
if len(args.RegistryName) != 0 {
foundRegistry := false
for _, registry := range registries {
if registry.Name == args.RegistryName {
foundRegistry = true
}
}
if !foundRegistry {
return bcode.ErrAddonRegistryNotExist.SetMessage(fmt.Sprintf("specified registry %s not exist", args.RegistryName))
}
}
for i, r := range registries {
if len(args.RegistryName) != 0 && args.RegistryName != r.Name {
continue
}
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
if err == nil {
return nil
}
@@ -471,8 +485,8 @@ func (u *addonServiceImpl) UpdateAddon(ctx context.Context, name string, args ap
return err
}
for _, r := range registries {
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache)
for i, r := range registries {
err = pkgaddon.EnableAddon(ctx, name, args.Version, u.kubeClient, u.discoveryClient, u.apply, u.config, r, args.Args, u.addonRegistryCache, pkgaddon.FilterDependencyRegistries(i, registries))
if err == nil {
return nil
}

View File

@@ -418,6 +418,7 @@ func (c *applicationServiceImpl) CreateApplicationTrigger(ctx context.Context, a
Type: req.Type,
PayloadType: req.PayloadType,
ComponentName: req.ComponentName,
Registry: req.Registry,
Token: genWebhookToken(),
}
if err := c.Store.Add(ctx, trigger); err != nil {

View File

@@ -215,13 +215,16 @@ func (c *customHandlerImpl) install() {
}
func (c *acrHandlerImpl) handle(ctx context.Context, webhookTrigger *model.ApplicationTrigger, app *model.Application) (interface{}, error) {
component, err := getComponent(ctx, c.w.Store, webhookTrigger)
if err != nil {
return nil, err
}
acrReq := c.req
image := fmt.Sprintf("registry.%s.aliyuncs.com/%s:%s", acrReq.Repository.Region, acrReq.Repository.RepoFullName, acrReq.PushData.Tag)
registry := webhookTrigger.Registry
if registry == "" {
registry = fmt.Sprintf("registry.%s.aliyuncs.com", acrReq.Repository.Region)
}
image := fmt.Sprintf("%s/%s:%s", registry, acrReq.Repository.RepoFullName, acrReq.PushData.Tag)
if err := c.w.patchComponentProperties(ctx, component, &runtime.RawExtension{
Raw: []byte(fmt.Sprintf(`{"image": "%s"}`, image)),
}); err != nil {

View File

@@ -196,6 +196,40 @@ var _ = Describe("Test application service function", func() {
Expect(err).Should(BeNil())
Expect((*comp.Properties)["image"]).Should(Equal("registry.test-region.aliyuncs.com/test-namespace/test-repo:test-tag"))
By("Test HandleApplicationWebhook function with ACR payload and registry info")
acrTrigger, err = appService.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{
Name: "test-acr",
PayloadType: "acr",
Type: "webhook",
ComponentName: "component-name-webhook",
Registry: "test-enterprise-registry.test-region.cr.aliyuncs.com",
})
Expect(err).Should(BeNil())
acrBody = apisv1.HandleApplicationTriggerACRRequest{
PushData: apisv1.ACRPushData{
Digest: "test-digest",
Tag: "test-tag",
},
Repository: apisv1.ACRRepository{
Name: "test-repo",
Namespace: "test-namespace",
Region: "test-region",
RepoFullName: "test-namespace/test-repo",
RepoType: "public",
},
}
body, err = json.Marshal(acrBody)
Expect(err).Should(BeNil())
httpreq, err = http.NewRequest("post", "/", bytes.NewBuffer(body))
httpreq.Header.Add(restful.HEADER_ContentType, "application/json")
Expect(err).Should(BeNil())
_, err = webhookService.HandleApplicationWebhook(context.TODO(), acrTrigger.Token, restful.NewRequest(httpreq))
Expect(err).Should(BeNil())
comp, err = appService.GetApplicationComponent(context.TODO(), appModel, "component-name-webhook")
Expect(err).Should(BeNil())
Expect((*comp.Properties)["image"]).Should(Equal("test-enterprise-registry.test-region.cr.aliyuncs.com/test-namespace/test-repo:test-tag"))
By("Test HandleApplicationWebhook function with harbor payload")
harborTrigger, err := appService.CreateApplicationTrigger(context.TODO(), appModel, apisv1.CreateApplicationTriggerRequest{
Name: "test-harbor",

View File

@@ -129,6 +129,8 @@ type EnableAddonRequest struct {
Clusters []string `json:"clusters,omitempty"`
// Version specify the version of addon to enable
Version string `json:"version,omitempty"`
// RegistryName specify the registry name
RegistryName string `json:"registryName,omitempty"`
}
// ListAddonResponse defines the format for addon list response
@@ -504,6 +506,7 @@ type CreateApplicationTriggerRequest struct {
Type string `json:"type" validate:"oneof=webhook"`
PayloadType string `json:"payloadType" validate:"checkpayloadtype"`
ComponentName string `json:"componentName,omitempty" optional:"true"`
Registry string `json:"registry,omitempty" optional:"true"`
}
// ApplicationTriggerBase application trigger base model

View File

@@ -73,6 +73,9 @@ var (
// ErrCloudShellNotInit means the cloudshell CR not created
ErrCloudShellNotInit = NewBcode(400, 50021, "Closing the console window and retry")
// ErrRegistryNotExist means the specified registry not exist
ErrRegistryNotExist = NewBcode(400, 50022, "The specified not exist")
)
// isGithubRateLimit check if error is github rate limit

View File

@@ -19,6 +19,7 @@ package component
import (
"context"
"encoding/json"
"fmt"
"strings"
"github.com/pkg/errors"
@@ -141,10 +142,18 @@ func SelectRefObjectsForDispatch(ctx context.Context, cli client.Client, appNs s
if err != nil {
return nil, err
}
isNamespaced, err := IsGroupVersionKindNamespaceScoped(cli.RESTMapper(), gvk)
if err != nil {
return nil, err
}
if selector.Name == "" && labelSelector != nil {
uns := &unstructured.UnstructuredList{}
uns.SetGroupVersionKind(gvk)
if err = cli.List(ctx, uns, client.InNamespace(ns), client.MatchingLabels(labelSelector)); err != nil {
opts := []client.ListOption{client.MatchingLabels(labelSelector)}
if isNamespaced {
opts = append(opts, client.InNamespace(ns))
}
if err = cli.List(ctx, uns, opts...); err != nil {
return nil, errors.Wrapf(err, "failed to load ref object %s with selector", gvk.Kind)
}
for _, _un := range uns.Items {
@@ -154,7 +163,9 @@ func SelectRefObjectsForDispatch(ctx context.Context, cli client.Client, appNs s
un := &unstructured.Unstructured{}
un.SetGroupVersionKind(gvk)
un.SetName(selector.Name)
un.SetNamespace(ns)
if isNamespaced {
un.SetNamespace(ns)
}
if selector.Name == "" {
un.SetName(compName)
}
@@ -248,3 +259,15 @@ func ConvertUnstructuredsToReferredObjects(uns []*unstructured.Unstructured) (re
}
return refObjs, nil
}
// IsGroupVersionKindNamespaceScoped check if the target GroupVersionKind is namespace scoped resource
func IsGroupVersionKindNamespaceScoped(mapper meta.RESTMapper, gvk schema.GroupVersionKind) (bool, error) {
mappings, err := mapper.RESTMappings(gvk.GroupKind(), gvk.Version)
if err != nil {
return false, err
}
if len(mappings) == 0 {
return false, fmt.Errorf("unable to fund the mappings for gvk %s", gvk)
}
return mappings[0].Scope.Name() == meta.RESTScopeNameNamespace, nil
}

View File

@@ -25,6 +25,7 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
utilfeature "k8s.io/apiserver/pkg/util/feature"
@@ -111,6 +112,10 @@ var _ = Describe("Test ref-objects functions", func() {
Namespace: "test",
Labels: map[string]string{"key": "value"},
},
}, &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "test-cluster-role",
},
}} {
Expect(k8sClient.Create(context.Background(), obj)).Should(Succeed())
}
@@ -119,25 +124,26 @@ var _ = Describe("Test ref-objects functions", func() {
Object: map[string]interface{}{
"apiVersion": apiVersion,
"kind": kind,
"metadata": map[string]interface{}{
"name": name,
"namespace": namespace,
},
"metadata": map[string]interface{}{"name": name},
},
}
if namespace != "" {
un.SetNamespace(namespace)
}
if labels != nil {
un.Object["metadata"].(map[string]interface{})["labels"] = labels
}
return un
}
testcases := map[string]struct {
Input v1alpha1.ObjectReferrer
compName string
appNs string
Output []*unstructured.Unstructured
Error string
Scope string
IsService bool
Input v1alpha1.ObjectReferrer
compName string
appNs string
Output []*unstructured.Unstructured
Error string
Scope string
IsService bool
IsClusterRole bool
}{
"normal": {
Input: v1alpha1.ObjectReferrer{
@@ -259,6 +265,16 @@ var _ = Describe("Test ref-objects functions", func() {
Scope: RefObjectsAvailableScopeCluster,
Error: "cannot refer to objects outside control plane",
},
"test-cluster-scope-resource": {
Input: v1alpha1.ObjectReferrer{
ObjectTypeIdentifier: v1alpha1.ObjectTypeIdentifier{Resource: "clusterrole"},
ObjectSelector: v1alpha1.ObjectSelector{Name: "test-cluster-role"},
},
appNs: "test",
Scope: RefObjectsAvailableScopeCluster,
Output: []*unstructured.Unstructured{createUnstructured("rbac.authorization.k8s.io/v1", "ClusterRole", "test-cluster-role", "", nil)},
IsClusterRole: true,
},
}
for name, tt := range testcases {
By("Test " + name)
@@ -276,6 +292,9 @@ var _ = Describe("Test ref-objects functions", func() {
Expect(output[0].Object["kind"]).Should(Equal("Service"))
Expect(output[0].Object["spec"].(map[string]interface{})["clusterIP"]).Should(BeNil())
} else {
if tt.IsClusterRole {
delete(output[0].Object, "rules")
}
Expect(output).Should(Equal(tt.Output))
}
}

View File

@@ -365,10 +365,9 @@ func (td *traitDef) Complete(ctx process.Context, abstractTemplate string, param
for _, auxiliary := range auxiliaries {
target := outputsPatcher.LookupPath(value.FieldPath(auxiliary.Name))
if !target.Exists() {
return errors.WithMessagef(err, "trait=%s, to=%s, invalid patch trait into auxiliary workload", td.name, auxiliary.Name)
continue
}
patcher := outputsPatcher.LookupPath(value.FieldPath(auxiliary.Name))
if err := auxiliary.Ins.Unify(patcher); err != nil {
if err = auxiliary.Ins.Unify(target); err != nil {
return errors.WithMessagef(err, "trait=%s, to=%s, invalid patch trait into auxiliary workload", td.name, auxiliary.Name)
}
}

View File

@@ -1480,3 +1480,57 @@ if len(context.outputs.ingress.status.loadBalancer.ingress) == 0 {
assert.Equal(t, ca.expMessage, gotMessage, message)
}
}
func TestTraitPatchSingleOutput(t *testing.T) {
baseTemplate := `
output: {
apiVersion: "apps/v1"
kind: "Deployment"
spec: selector: matchLabels: "app.oam.dev/component": context.name
}
outputs: gameconfig: {
apiVersion: "v1"
kind: "ConfigMap"
metadata: name: context.name + "game-config"
data: {}
}
outputs: sideconfig: {
apiVersion: "v1"
kind: "ConfigMap"
metadata: name: context.name + "side-config"
data: {}
}
parameter: {}
`
traitTemplate := `
patchOutputs: sideconfig: data: key: "val"
parameter: {}
`
ctx := process.NewContext(process.ContextData{
AppName: "myapp",
CompName: "test",
Namespace: "default",
AppRevisionName: "myapp-v1",
})
wt := NewWorkloadAbstractEngine("-", &packages.PackageDiscover{})
if err := wt.Complete(ctx, baseTemplate, map[string]interface{}{}); err != nil {
t.Error(err)
return
}
td := NewTraitAbstractEngine("single-patch", &packages.PackageDiscover{})
r := require.New(t)
err := td.Complete(ctx, traitTemplate, map[string]string{})
r.NoError(err)
base, assists := ctx.Output()
r.NotNil(base)
r.Equal(2, len(assists))
got, err := assists[1].Ins.Unstructured()
r.NoError(err)
val, ok, err := unstructured.NestedString(got.Object, "data", "key")
r.NoError(err)
r.True(ok)
r.Equal("val", val)
}

View File

@@ -51,6 +51,9 @@ func TestCreateEnv(t *testing.T) {
var err error
cfg, err = testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
assert.NoError(t, clientgoscheme.AddToScheme(testScheme))
rawClient, err = client.New(cfg, client.Options{Scheme: testScheme})
@@ -95,4 +98,5 @@ func TestCreateEnv(t *testing.T) {
}
})
}
}

View File

@@ -148,6 +148,8 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
vela addon enable <your-local-addon-path>
Enable addon with specified args (the args should be defined in addon's parameters):
vela addon enable <addon-name> <my-parameter-of-addon>=<my-value>
Enable addon with specified registry:
vela addon enable <registryName>/<addonName>
`,
RunE: func(cmd *cobra.Command, args []string) error {
@@ -543,10 +545,27 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
if err != nil {
return err
}
for _, registry := range registries {
registryName, addonName, err := splitSpecifyRegistry(name)
if err != nil {
return err
}
if len(registryName) != 0 {
foundRegistry := false
for _, registry := range registries {
if registry.Name == registryName {
foundRegistry = true
}
}
if !foundRegistry {
return fmt.Errorf("specified registry %s not exist", registryName)
}
}
for i, registry := range registries {
opts := addonOptions()
err = pkgaddon.EnableAddon(ctx, name, version, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, opts...)
if len(registryName) != 0 && registryName != registry.Name {
continue
}
err = pkgaddon.EnableAddon(ctx, addonName, version, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, pkgaddon.FilterDependencyRegistries(i, registries), opts...)
if errors.Is(err, pkgaddon.ErrNotExist) {
continue
}
@@ -558,21 +577,24 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
}
input := NewUserInput()
if input.AskBool(unMatchErr.Error(), &UserInputOptions{AssumeYes: false}) {
err = pkgaddon.EnableAddon(ctx, name, availableVersion, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil)
err = pkgaddon.EnableAddon(ctx, addonName, availableVersion, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, pkgaddon.FilterDependencyRegistries(i, registries))
return err
}
// The user does not agree to use the version provided by us
return fmt.Errorf("you can try another version by command: \"vela addon enable %s --version <version> \" ", name)
return fmt.Errorf("you can try another version by command: \"vela addon enable %s --version <version> \" ", addonName)
}
if err != nil {
return err
}
if err = waitApplicationRunning(k8sClient, name); err != nil {
if err = waitApplicationRunning(k8sClient, addonName); err != nil {
return err
}
return nil
}
return fmt.Errorf("addon: %s not found in registries", name)
if len(registryName) != 0 {
return fmt.Errorf("addon: %s not found in registry %s", addonName, registryName)
}
return fmt.Errorf("addon: %s not found in all candidate registries", addonName)
}
func addonOptions() []pkgaddon.InstallOption {
@@ -1136,3 +1158,15 @@ func NewAddonPackageCommand(c common.Args) *cobra.Command {
}
return cmd
}
func splitSpecifyRegistry(name string) (string, string, error) {
res := strings.Split(name, "/")
switch len(res) {
case 2:
return res[0], res[1], nil
case 1:
return "", res[0], nil
default:
return "", "", fmt.Errorf("invalid addon name, you should specify name only <addonName> or with registry as prefix <registryName>/<addonName>")
}
}

View File

@@ -443,3 +443,37 @@ func TestNewAddonCreateCommand(t *testing.T) {
_ = os.RemoveAll("test-addon")
}
func TestCheckSpecifyRegistry(t *testing.T) {
testCases := []struct {
name string
registry string
addonName string
hasError bool
}{
{
name: "fluxcd",
registry: "",
addonName: "fluxcd",
hasError: false,
},
{
name: "kubevela/fluxcd",
registry: "kubevela",
addonName: "fluxcd",
hasError: false,
},
{
name: "test/kubevela/fluxcd",
registry: "",
addonName: "",
hasError: true,
},
}
for _, testCase := range testCases {
r, n, err := splitSpecifyRegistry(testCase.name)
assert.Equal(t, err != nil, testCase.hasError)
assert.Equal(t, r, testCase.registry)
assert.Equal(t, n, testCase.addonName)
}
}

View File

@@ -33,6 +33,9 @@ func TestInfo(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
info := NewInfo()
info.Init(cfg)
assert.Equal(t, info.GetColumnCount(), 7)

View File

@@ -38,6 +38,9 @@ func TestApp(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -38,6 +38,10 @@ func TestTopologyView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -39,6 +39,10 @@ func TestApplicationView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -38,6 +38,10 @@ func TestClusterNamespaceView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")
@@ -83,4 +87,5 @@ func TestClusterNamespaceView(t *testing.T) {
cnsView.Table.Table = cnsView.Table.Select(1, 1)
assert.Empty(t, cnsView.managedResourceView(nil))
})
}

View File

@@ -39,6 +39,10 @@ func TestClusterView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -39,6 +39,10 @@ func TestContainerView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -36,6 +36,10 @@ func TestHelpView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -39,6 +39,10 @@ func TestLogView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -39,6 +39,10 @@ func TestManagedResourceView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -39,6 +39,10 @@ func TestNamespaceView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -36,6 +36,10 @@ func TestPageStack(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -39,6 +39,10 @@ func TestPodView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -38,6 +38,10 @@ func TestYamlView(t *testing.T) {
}
cfg, err := testEnv.Start()
assert.NoError(t, err)
defer func() {
assert.NoError(t, testEnv.Stop())
}()
testClient, err := client.New(cfg, client.Options{Scheme: common.Scheme})
assert.NoError(t, err)
app := NewApp(testClient, cfg, "")

View File

@@ -0,0 +1,23 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: example-app-rollout
namespace: default
spec:
components:
- name: hello-world-server
type: webservice
properties:
image: crccheck/hello-world
ports:
- port: 8000
expose: true
type: webservice
policies:
- name: health-policy-demo
type: health
properties:
probeInterval: 5
probeTimeout: 10
```

View File

@@ -0,0 +1,29 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: busybox
spec:
components:
- name: busybox
type: webservice
properties:
image: busybox
cmd: ["sleep", "86400"]
labels:
label-key: label-value
to-delete-label-key: to-delete-label-value
traits:
- type: affinity
properties:
podAffinity:
preferred:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: "secrity"
values: ["S1"]
namespaces: ["default"]
topologyKey: "kubernetes.io/hostname"
```

View File

@@ -0,0 +1,41 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: busybox
spec:
components:
- name: busybox
type: webservice
properties:
image: busybox
cmd: ["sleep", "86400"]
labels:
pod-label-key: pod-label-value
to-delete-label-key: to-delete-label-value
traits:
# the json merge patch can be used to add, replace and delete fields
# the following part will
# 1. add `deploy-label-key` to deployment labels
# 2. set deployment replicas to 3
# 3. set `pod-label-key` to `pod-label-modified-value` in pod labels
# 4. delete `to-delete-label-key` in pod labels
# 5. reset `containers` for pod
- type: json-merge-patch
properties:
metadata:
labels:
deploy-label-key: deploy-label-added-value
spec:
replicas: 3
template:
metadata:
labels:
pod-label-key: pod-label-modified-value
to-delete-label-key: null
spec:
containers:
- name: busybox-new
image: busybox:1.34
command: ["sleep", "864000"]
```

View File

@@ -0,0 +1,41 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: busybox
spec:
components:
- name: busybox
type: webservice
properties:
image: busybox
cmd: ["sleep", "86400"]
labels:
pod-label-key: pod-label-value
to-delete-label-key: to-delete-label-value
traits:
# the json patch can be used to add, replace and delete fields
# the following part will
# 1. add `deploy-label-key` to deployment labels
# 2. set deployment replicas to 3
# 3. set `pod-label-key` to `pod-label-modified-value` in pod labels
# 4. delete `to-delete-label-key` in pod labels
# 5. add sidecar container for pod
- type: json-patch
properties:
operations:
- op: add
path: "/spec/replicas"
value: 3
- op: replace
path: "/spec/template/metadata/labels/pod-label-key"
value: pod-label-modified-value
- op: remove
path: "/spec/template/metadata/labels/to-delete-label-key"
- op: add
path: "/spec/template/spec/containers/1"
value:
name: busybox-sidecar
image: busybox:1.34
command: ["sleep", "864000"]
```

View File

@@ -0,0 +1,40 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: application-with-topologyspreadconstraints
spec:
components:
- name: busybox-runner
type: worker
properties:
image: busybox
cmd:
- sleep
- '1000'
traits:
- type: topologyspreadconstraints
properties:
constraints:
- topologyKey: zone
labelSelector:
matchLabels:
zone: us-east-1a
maxSkew: 1
whenUnsatisfiable: DoNotSchedule
minDomains: 1
nodeAffinityPolicy: Ignore
nodeTaintsPolicy: Ignore
- topologyKey: node
labelSelector:
matchExpressions:
- key: foo
operator: In
values:
- abc
maxSkew: 1
whenUnsatisfiable: ScheduleAnyway
minDomains: 1
nodeAffinityPolicy: Ignore
nodeTaintsPolicy: Ignore
```

View File

@@ -0,0 +1,32 @@
```yaml
kind: Application
apiVersion: core.oam.dev/v1beta1
metadata:
name: test-config
namespace: "config-e2e-test"
spec:
components: []
workflow:
steps:
- name: write-config
type: create-config
properties:
name: test
config:
key1: value1
key2: 2
key3: true
key4:
key5: value5
- name: read-config
type: read-config
properties:
name: test
outputs:
- fromKey: config
name: read-config
- name: delete-config
type: delete-config
properties:
name: test
```

View File

@@ -0,0 +1,25 @@
```yaml
kind: Application
apiVersion: core.oam.dev/v1beta1
metadata:
name: test-config
namespace: "config-e2e-test"
spec:
components: []
workflow:
steps:
- name: write-config
type: create-config
properties:
name: test
config:
key1: value1
key2: 2
key3: true
key4:
key5: value5
- name: delete-config
type: delete-config
properties:
name: test
```

View File

@@ -0,0 +1,80 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: rds-app
namespace: project-1
spec:
components:
- name: db
type: alibaba-rds
properties:
instance_name: db
account_name: kubevela
password: my-password
writeConnectionSecretToRef:
name: project-1-rds-conn-credential
policies:
- name: env-policy
type: env-binding
properties:
envs:
# 部署 RDS 给杭州集群
- name: hangzhou
placement:
clusterSelector:
name: cluster-hangzhou
patch:
components:
- name: db
type: alibaba-rds
properties:
# region: hangzhou
instance_name: hangzhou_db
# 部署 RDS 给香港集群
- name: hongkong
placement:
clusterSelector:
name: cluster-hongkong
namespaceSelector:
name: hk-project-1
patch:
components:
- name: db
type: alibaba-rds
properties:
# region: hongkong
instance_name: hongkong_db
writeConnectionSecretToRef:
name: hk-project-rds-credential
workflow:
steps:
# 部署 RDS 给杭州区用
- name: deploy-hangzhou-rds
type: deploy-cloud-resource
properties:
env: hangzhou
# 将给杭州区用的 RDS 共享给北京区
- name: share-hangzhou-rds-to-beijing
type: share-cloud-resource
properties:
env: hangzhou
placements:
- cluster: cluster-beijing
# 部署 RDS 给香港区用
- name: deploy-hongkong-rds
type: deploy-cloud-resource
properties:
env: hongkong
# 将给香港区用的 RDS 共享给香港区其他项目用
- name: share-hongkong-rds-to-other-namespace
type: share-cloud-resource
properties:
env: hongkong
placements:
- cluster: cluster-hongkong
namespace: hk-project-2
- cluster: cluster-hongkong
namespace: hk-project-3
```

View File

@@ -0,0 +1,42 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: jdbc
spec:
components:
- name: db
type: alibaba-rds
properties:
instance_name: favorite-links
database_name: db1
account_name: oamtest
password: U34rfwefwefffaked
security_ips: [ "0.0.0.0/0" ]
privilege: ReadWrite
writeConnectionSecretToRef:
name: db-conn
- name: express-server
type: webservice
properties:
image: crccheck/hello-world
port: 8000
workflow:
steps:
- name: jdbc
type: generate-jdbc-connection
outputs:
- name: jdbc
valueFrom: jdbc
properties:
name: db-conn
namespace: default
- name: apply
type: apply-component
inputs:
- from: jdbc
parameterKey: env
properties:
component: express-server
```

View File

@@ -0,0 +1,50 @@
```yaml
apiVersion: core.oam.dev/v1alpha1
kind: WorkflowRun
metadata:
name: observability
namespace: vela-system
spec:
context:
readConfig: true
mode:
workflowSpec:
steps:
- name: Enable Prism
type: addon-operation
properties:
addonName: vela-prism
- name: Enable o11y
type: addon-operation
properties:
addonName: o11y-definitions
operation: enable
args:
- --override-definitions
- name: Prepare Prometheus
type: step-group
subSteps:
- name: get-exist-prometheus
type: list-config
properties:
template: prometheus-server
outputs:
- name: prometheus
valueFrom: "output.configs"
- name: prometheus-server
inputs:
- from: prometheus
# TODO: Make it is not required
parameterKey: configs
if: "!context.readConfig || len(inputs.prometheus) == 0"
type: addon-operation
properties:
addonName: prometheus-server
operation: enable
args:
- memory=4096Mi
- serviceType=LoadBalancer
```

View File

@@ -0,0 +1,18 @@
```yaml
kind: Application
apiVersion: core.oam.dev/v1beta1
metadata:
name: test-config
namespace: "config-e2e-test"
spec:
components: []
workflow:
steps:
- name: read-config
type: read-config
properties:
name: test
outputs:
- fromKey: config
name: read-config
```

View File

@@ -0,0 +1,80 @@
```yaml
apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
name: rds-app
namespace: project-1
spec:
components:
- name: db
type: alibaba-rds
properties:
instance_name: db
account_name: kubevela
password: my-password
writeConnectionSecretToRef:
name: project-1-rds-conn-credential
policies:
- name: env-policy
type: env-binding
properties:
envs:
# 部署 RDS 给杭州集群
- name: hangzhou
placement:
clusterSelector:
name: cluster-hangzhou
patch:
components:
- name: db
type: alibaba-rds
properties:
# region: hangzhou
instance_name: hangzhou_db
# 部署 RDS 给香港集群
- name: hongkong
placement:
clusterSelector:
name: cluster-hongkong
namespaceSelector:
name: hk-project-1
patch:
components:
- name: db
type: alibaba-rds
properties:
# region: hongkong
instance_name: hongkong_db
writeConnectionSecretToRef:
name: hk-project-rds-credential
workflow:
steps:
# 部署 RDS 给杭州区用
- name: deploy-hangzhou-rds
type: deploy-cloud-resource
properties:
env: hangzhou
# 将给杭州区用的 RDS 共享给北京区
- name: share-hangzhou-rds-to-beijing
type: share-cloud-resource
properties:
env: hangzhou
placements:
- cluster: cluster-beijing
# 部署 RDS 给香港区用
- name: deploy-hongkong-rds
type: deploy-cloud-resource
properties:
env: hongkong
# 将给香港区用的 RDS 共享给香港区其他项目用
- name: share-hongkong-rds-to-other-namespace
type: share-cloud-resource
properties:
env: hongkong
placements:
- cluster: cluster-hongkong
namespace: hk-project-2
- cluster: cluster-hongkong
namespace: hk-project-3
```

View File

@@ -47,6 +47,7 @@ const AllComponentTypes = "*"
type MarkdownReference struct {
Filter func(types.Capability) bool
AllInOne bool
ForceExample bool
CustomDocHeader string
DiscoveryMapper discoverymapper.DiscoveryMapper
ParseReference
@@ -255,6 +256,9 @@ func (ref *MarkdownReference) GenerateMarkdownForCap(ctx context.Context, c type
if sampleContent != "" {
sample = fmt.Sprintf("\n\n%s %s\n\n%s", sharp, exampleTitle, sampleContent)
} else if ref.ForceExample {
fmt.Printf("You must provide example doc for the new added definition \"%s\", place the example doc in the /refereces/docgen/def-doc folders, for more details refer to https://kubevela.io/docs/contributor/cli-ref-doc#how-the-docs-generated", capName)
os.Exit(1)
}
if c.Category == types.CUECategory && baseDoc != "" {
base = fmt.Sprintf("\n\n%s %s\n\n%s", sharp, baseTitle, baseDoc)

View File

@@ -232,4 +232,35 @@ var _ = Describe("Test addon rest api", func() {
}, 30*time.Second, 300*time.Millisecond).Should(Succeed())
})
})
Describe("Test addon dependency addon in other registry", func() {
It("Test Operation of enable addon from other registry", func() {
req := apisv1.EnableAddonRequest{}
res := post("/addons/mock-dep-addon/enable", req)
defer res.Body.Close()
var addon apisv1.AddonStatusResponse
Expect(decodeResponseBody(res, &addon)).Should(Succeed())
Expect(addon.Name).Should(BeEquivalentTo("mock-dep-addon"))
Eventually(func(g Gomega) {
status := get("/addons/mock-dep-addon/status")
var newaddonStatus apisv1.AddonStatusResponse
g.Expect(decodeResponseBody(status, &newaddonStatus)).Should(Succeed())
g.Expect(newaddonStatus.Name).Should(BeEquivalentTo("mock-dep-addon"))
g.Expect(newaddonStatus.InstalledVersion).Should(BeEquivalentTo("v1.0.0"))
g.Expect(newaddonStatus.Phase).Should(BeEquivalentTo(apisv1.AddonPhaseEnabled))
}, 30*time.Second, 300*time.Millisecond).Should(Succeed())
})
})
Describe("Test enable an addon with specified registry", func() {
It("Test with a not exist registry", func() {
req := apisv1.EnableAddonRequest{
RegistryName: "not-exist",
}
res := post("/addons/test-addon/enable", req)
defer res.Body.Close()
Expect(res.StatusCode).Should(BeEquivalentTo(400))
})
})
})