Compare commits

..

11 Commits

Author SHA1 Message Date
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
41 changed files with 1270 additions and 146 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

@@ -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,8 @@ const (
// HelmRepository is the config type for Helm chart repository
HelmRepository = "config-helm-repository"
)
const (
// TerrfaormComponentPrefix is the prefix of component type of terraform-xxx
TerrfaormComponentPrefix = "terraform-"
)

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:

View File

@@ -455,7 +455,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:

View File

@@ -455,7 +455,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))

2
go.mod
View File

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

2
go.sum
View File

@@ -1437,6 +1437,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=

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

@@ -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,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
"strings"
set "github.com/deckarep/golang-set"
"github.com/pkg/errors"
@@ -134,12 +135,27 @@ func (u *configUseCaseImpl) GetConfigType(ctx context.Context, configType string
}
func (u *configUseCaseImpl) CreateConfig(ctx context.Context, req apis.CreateConfigRequest) error {
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.TerrfaormComponentPrefix) {
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)
}
app := v1beta1.Application{
ObjectMeta: metav1.ObjectMeta{
Name: req.Name,
Namespace: types.DefaultKubeVelaNS,
Annotations: map[string]string{
types.AnnotationConfigAlias: req.Alias,
types.AnnotationConfigAlias: req.Alias,
types.AnnotationConfigDescription: req.Description,
},
Labels: map[string]string{
model.LabelSourceOfTruth: model.FromInner,
@@ -153,7 +169,7 @@ func (u *configUseCaseImpl) CreateConfig(ctx context.Context, req apis.CreateCon
{
Name: req.Name,
Type: req.ComponentType,
Properties: &runtime.RawExtension{Raw: []byte(req.Properties)},
Properties: &runtime.RawExtension{Raw: []byte(p)},
},
},
},
@@ -201,12 +217,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),
}
configs[i] = retrieveConfigFromApplication(a, a.Labels[types.LabelConfigProject])
switch a.Status.Phase {
case common.ApplicationRunning:
configs[i].Status = configIsReady

View File

@@ -236,6 +236,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 +257,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 {

View File

@@ -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.TerrfaormComponentPrefix) {
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:
@@ -616,3 +598,15 @@ func ConvertProjectUserModel2Base(user *model.ProjectUser) *apisv1.ProjectUserBa
}
return base
}
func retrieveConfigFromApplication(a v1beta1.Application, project string) *apisv1.Config {
return &apisv1.Config{
ConfigType: a.Labels[types.LabelConfigType],
Name: a.Name,
Project: project,
CreatedTime: &(a.CreationTimestamp.Time),
ApplicationStatus: a.Status.Phase,
Alias: a.Annotations[types.AnnotationConfigAlias],
Description: a.Annotations[types.AnnotationConfigDescription],
}
}

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

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

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

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

@@ -193,7 +193,7 @@ 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("failed to authenticate Terraform cloud provider %s", providerType)
}
providerAppName := fmt.Sprintf("config-terraform-provider-%s", name)
a := &v1beta1.Application{}
@@ -217,12 +217,12 @@ func prepareProviderAddSubCommand(c common.Args, ioStreams cmdutil.IOStreams) ([
},
}
if err := k8sClient.Create(ctx, a); err != nil {
return fmt.Errorf("failed to authentiate Terraform cloud provier %s", providerType)
return fmt.Errorf("failed to authenticate Terraform cloud provider %s", providerType)
}
ioStreams.Infof("Successfully authentiate provider %s for %s\n", name, providerType)
ioStreams.Infof("Successfully authenticate provider %s for %s\n", name, providerType)
return nil
}
return fmt.Errorf("failed to authentiate Terraform cloud provier %s", providerType)
return fmt.Errorf("failed to authenticate Terraform cloud provider %s", providerType)
}
return fmt.Errorf("terraform provider %s for %s already exists", name, providerType)
}

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"

View File

@@ -501,7 +501,7 @@ template: {
readinessProbe?: #HealthProbe
// +usage=Specify the hostAliases to add
hostAliases: [...{
hostAliases?: [...{
ip: string
hostnames: [...string]
}]