mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-23 22:33:58 +00:00
Compare commits
16 Commits
v1.4.7-pat
...
v1.4.14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35a84e9cbf | ||
|
|
bf251d5039 | ||
|
|
31f0b28d96 | ||
|
|
810c47545e | ||
|
|
293f38dd84 | ||
|
|
1cf2cd23d2 | ||
|
|
69cf083d4a | ||
|
|
7df2b34a0b | ||
|
|
2a03e16098 | ||
|
|
110927ed97 | ||
|
|
90fbfa0f81 | ||
|
|
7fb045328d | ||
|
|
766c5852c6 | ||
|
|
4cb9a14b18 | ||
|
|
9a1e75cf48 | ||
|
|
192dc8966d |
2
.github/workflows/apiserver-test.yaml
vendored
2
.github/workflows/apiserver-test.yaml
vendored
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
|
||||
apiserver-unit-tests:
|
||||
runs-on: aliyun
|
||||
runs-on: aliyun-legacy
|
||||
needs: [ detect-noop,set-k8s-matrix ]
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
strategy:
|
||||
|
||||
6
.github/workflows/e2e-multicluster-test.yml
vendored
6
.github/workflows/e2e-multicluster-test.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
|
||||
|
||||
e2e-multi-cluster-tests:
|
||||
runs-on: aliyun
|
||||
runs-on: aliyun-legacy
|
||||
needs: [ detect-noop,set-k8s-matrix ]
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
strategy:
|
||||
@@ -97,7 +97,9 @@ jobs:
|
||||
kubectl cluster-info
|
||||
|
||||
- name: Load Image to kind cluster (Hub)
|
||||
run: make kind-load
|
||||
run: |
|
||||
make kind-load
|
||||
make kind-load-runtime-cluster
|
||||
|
||||
- name: Cleanup for e2e tests
|
||||
run: |
|
||||
|
||||
2
.github/workflows/e2e-rollout-test.yml
vendored
2
.github/workflows/e2e-rollout-test.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
||||
fi
|
||||
|
||||
e2e-rollout-tests:
|
||||
runs-on: aliyun
|
||||
runs-on: aliyun-legacy
|
||||
needs: [ detect-noop,set-k8s-matrix ]
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
strategy:
|
||||
|
||||
2
.github/workflows/e2e-test.yml
vendored
2
.github/workflows/e2e-test.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
||||
fi
|
||||
|
||||
e2e-tests:
|
||||
runs-on: aliyun
|
||||
runs-on: aliyun-legacy
|
||||
needs: [ detect-noop,set-k8s-matrix ]
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
strategy:
|
||||
|
||||
2
.github/workflows/go.yml
vendored
2
.github/workflows/go.yml
vendored
@@ -98,7 +98,7 @@ jobs:
|
||||
version: ${{ env.GOLANGCI_VERSION }}
|
||||
|
||||
check-diff:
|
||||
runs-on: aliyun
|
||||
runs-on: aliyun-legacy
|
||||
needs: detect-noop
|
||||
if: needs.detect-noop.outputs.noop != 'true'
|
||||
|
||||
|
||||
2
.github/workflows/timed-task.yml
vendored
2
.github/workflows/timed-task.yml
vendored
@@ -4,7 +4,7 @@ on:
|
||||
- cron: '* * * * *'
|
||||
jobs:
|
||||
clean-image:
|
||||
runs-on: aliyun
|
||||
runs-on: aliyun-legacy
|
||||
steps:
|
||||
- name: Cleanup image
|
||||
run: docker image prune -f
|
||||
6
Makefile
6
Makefile
@@ -83,15 +83,17 @@ endif
|
||||
|
||||
|
||||
# load docker image to the kind cluster
|
||||
kind-load: kind-load-runtime-cluster
|
||||
kind-load: kind-load-rollout
|
||||
docker build -t $(VELA_CORE_TEST_IMAGE) -f Dockerfile.e2e .
|
||||
kind load docker-image $(VELA_CORE_TEST_IMAGE) || { echo >&2 "kind not installed or error loading image: $(VELA_CORE_TEST_IMAGE)"; exit 1; }
|
||||
|
||||
kind-load-runtime-cluster:
|
||||
kind-load-rollout:
|
||||
/bin/sh hack/e2e/build_runtime_rollout.sh
|
||||
docker build -t $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) -f runtime/rollout/e2e/Dockerfile.e2e runtime/rollout/e2e/
|
||||
rm -rf runtime/rollout/e2e/tmp
|
||||
kind load docker-image $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) || { echo >&2 "kind not installed or error loading image: $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE)"; exit 1; }
|
||||
|
||||
kind-load-runtime-cluster:
|
||||
kind load docker-image $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE) --name=$(RUNTIME_CLUSTER_NAME) || { echo >&2 "kind not installed or error loading image: $(VELA_RUNTIME_ROLLOUT_TEST_IMAGE)"; exit 1; }
|
||||
|
||||
# Run tests
|
||||
|
||||
@@ -19,6 +19,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
goflag "flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -138,6 +139,7 @@ func main() {
|
||||
flag.DurationVar(&clusterMetricsInterval, "cluster-metrics-interval", 15*time.Second, "The interval that ClusterMetricsMgr will collect metrics from clusters, default value is 15 seconds.")
|
||||
flag.BoolVar(&controllerArgs.EnableCompatibility, "enable-asi-compatibility", false, "enable compatibility for asi")
|
||||
flag.BoolVar(&controllerArgs.IgnoreAppWithoutControllerRequirement, "ignore-app-without-controller-version", false, "If true, application controller will not process the app without 'app.oam.dev/controller-version-require' annotation")
|
||||
flag.BoolVar(&controllerArgs.IgnoreDefinitionWithoutControllerRequirement, "ignore-definition-without-controller-version", false, "If true, trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation")
|
||||
standardcontroller.AddOptimizeFlags()
|
||||
standardcontroller.AddAdmissionFlags()
|
||||
flag.IntVar(&resourcekeeper.MaxDispatchConcurrent, "max-dispatch-concurrent", 10, "Set the max dispatch concurrent number, default is 10")
|
||||
@@ -146,9 +148,10 @@ func main() {
|
||||
flag.IntVar(&custom.MaxWorkflowStepErrorRetryTimes, "max-workflow-step-error-retry-times", 10, "Set the max workflow step error retry times, default is 10")
|
||||
utilfeature.DefaultMutableFeatureGate.AddFlag(flag.CommandLine)
|
||||
|
||||
flag.Parse()
|
||||
// setup logging
|
||||
klog.InitFlags(nil)
|
||||
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
|
||||
flag.Parse()
|
||||
if logDebug {
|
||||
_ = flag.Set("v", strconv.Itoa(int(commonconfig.LogDebug)))
|
||||
}
|
||||
|
||||
4
go.mod
4
go.mod
@@ -281,9 +281,9 @@ require (
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/net v0.0.0-20220516155154-20f960328961 // indirect
|
||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c // indirect
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
|
||||
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a // indirect
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
|
||||
6
go.sum
6
go.sum
@@ -2371,8 +2371,9 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220516155154-20f960328961 h1:+W/iTMPG0EL7aW+/atntZwZrvSRIj3m3yX414dSULUU=
|
||||
golang.org/x/net v0.0.0-20220516155154-20f960328961/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo=
|
||||
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -2557,8 +2558,9 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a h1:N2T1jUrTQE9Re6TFF5PhvEHXHCguynGhKjWVsIUt5cY=
|
||||
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
||||
@@ -77,7 +77,7 @@ ifeq (, $(shell which readme-generator))
|
||||
@{ \
|
||||
set -e ;\
|
||||
echo 'installing readme-generator-for-helm' ;\
|
||||
npm install -g readme-generator-for-helm ;\
|
||||
npm install -g @bitnami/readme-generator-for-helm ;\
|
||||
}
|
||||
else
|
||||
@$(OK) readme-generator-for-helm is already installed
|
||||
|
||||
@@ -86,7 +86,6 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
})
|
||||
It("should return one valid result, matching one registry", func() {
|
||||
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"velaux"}, []string{"KubeVela"})
|
||||
@@ -94,7 +93,6 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -113,10 +111,8 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
Expect(res).To(HaveLen(2))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
Expect(res[1].Name).To(Equal("traefik"))
|
||||
Expect(res[1].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[1].APISchema).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -127,7 +123,6 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -32,6 +32,7 @@ const (
|
||||
// SystemInfo systemInfo model
|
||||
type SystemInfo struct {
|
||||
BaseModel
|
||||
SignedKey string `json:"signedKey"`
|
||||
InstallID string `json:"installID"`
|
||||
EnableCollection bool `json:"enableCollection"`
|
||||
LoginType string `json:"loginType"`
|
||||
|
||||
@@ -57,7 +57,8 @@ const (
|
||||
GrantTypeRefresh = "refresh"
|
||||
)
|
||||
|
||||
var signedKey = ""
|
||||
// signedKey is the signed key of JWT
|
||||
var signedKey string
|
||||
|
||||
// AuthenticationService is the service of authentication
|
||||
type AuthenticationService interface {
|
||||
|
||||
@@ -63,6 +63,7 @@ func (u systemInfoServiceImpl) Get(ctx context.Context) (*model.SystemInfo, erro
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
info.SignedKey = rand.String(32)
|
||||
installID := rand.String(16)
|
||||
info.InstallID = installID
|
||||
info.EnableCollection = true
|
||||
@@ -159,7 +160,7 @@ func (u systemInfoServiceImpl) Init(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signedKey = info.InstallID
|
||||
signedKey = info.SignedKey
|
||||
_, err = initDexConfig(ctx, u.KubeClient, "http://velaux.com")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ func (c Condition) Validate() error {
|
||||
if c.JSONKey == "" {
|
||||
return fmt.Errorf("the json key of the condition can not be empty")
|
||||
}
|
||||
if c.Action != "enable" && c.Action != "disable" {
|
||||
return fmt.Errorf("the action of the condition must be enable or disable")
|
||||
if c.Action != "enable" && c.Action != "disable" && c.Action != "" {
|
||||
return fmt.Errorf("the action of the condition only supports enable, disable or leave it empty")
|
||||
}
|
||||
if c.Op != "" && !StringsContain([]string{"==", "!=", "in"}, c.Op) {
|
||||
return fmt.Errorf("the op of the condition must be `==` 、`!=` and `in`")
|
||||
|
||||
@@ -365,6 +365,9 @@ func (p *Parser) parsePoliciesFromRevision(ctx context.Context, af *Appfile) (er
|
||||
return err
|
||||
}
|
||||
for _, policy := range af.Policies {
|
||||
if policy.Properties == nil && policy.Type != v1alpha1.DebugPolicyType {
|
||||
return fmt.Errorf("policy %s named %s must not have empty properties", policy.Type, policy.Name)
|
||||
}
|
||||
switch policy.Type {
|
||||
case v1alpha1.GarbageCollectPolicyType:
|
||||
case v1alpha1.ApplyOncePolicyType:
|
||||
@@ -390,6 +393,9 @@ func (p *Parser) parsePolicies(ctx context.Context, af *Appfile) (err error) {
|
||||
return err
|
||||
}
|
||||
for _, policy := range af.Policies {
|
||||
if policy.Properties == nil && policy.Type != v1alpha1.DebugPolicyType {
|
||||
return fmt.Errorf("policy %s named %s must not have empty properties", policy.Type, policy.Name)
|
||||
}
|
||||
switch policy.Type {
|
||||
case v1alpha1.GarbageCollectPolicyType:
|
||||
case v1alpha1.ApplyOncePolicyType:
|
||||
|
||||
@@ -243,6 +243,20 @@ spec:
|
||||
image: "busybox"
|
||||
`
|
||||
|
||||
const appfileYamlEmptyPolicy = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: application-sample
|
||||
namespace: default
|
||||
spec:
|
||||
components: []
|
||||
policies:
|
||||
- type: garbage-collect
|
||||
name: somename
|
||||
properties:
|
||||
`
|
||||
|
||||
var _ = Describe("Test application parser", func() {
|
||||
It("Test we can parse an application to an appFile", func() {
|
||||
o := v1beta1.Application{}
|
||||
@@ -282,6 +296,14 @@ var _ = Describe("Test application parser", func() {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
_, err = NewApplicationParser(&tclient, dm, pd).GenerateAppFile(context.TODO(), ¬found)
|
||||
Expect(err).Should(HaveOccurred())
|
||||
|
||||
By("app with empty policy")
|
||||
emptyPolicy := v1beta1.Application{}
|
||||
err = yaml.Unmarshal([]byte(appfileYamlEmptyPolicy), &emptyPolicy)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
_, err = NewApplicationParser(&tclient, dm, pd).GenerateAppFile(context.TODO(), &emptyPolicy)
|
||||
Expect(err).Should(HaveOccurred())
|
||||
Expect(err.Error()).Should(ContainSubstring("have empty properties"))
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -86,4 +86,7 @@ type Args struct {
|
||||
|
||||
// IgnoreAppWithoutControllerRequirement indicates that application controller will not process the app without 'app.oam.dev/controller-version-require' annotation.
|
||||
IgnoreAppWithoutControllerRequirement bool
|
||||
|
||||
// IgnoreDefinitionWithoutControllerRequirement indicates that trait/component/workflowstep definition controller will not process the definition without 'definition.oam.dev/controller-version-require' annotation.
|
||||
IgnoreDefinitionWithoutControllerRequirement bool
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
if annotations := app.GetAnnotations(); annotations == nil || annotations[oam.AnnotationKubeVelaVersion] == "" {
|
||||
metav1.SetMetaDataAnnotation(&app.ObjectMeta, oam.AnnotationKubeVelaVersion, version.VelaVersion)
|
||||
}
|
||||
logCtx.AddTag("publish_version", app.GetAnnotations()[oam.AnnotationKubeVelaVersion])
|
||||
logCtx.AddTag("publish_version", app.GetAnnotations()[oam.AnnotationPublishVersion])
|
||||
|
||||
appParser := appfile.NewApplicationParser(r.Client, r.dm, r.pd)
|
||||
handler, err := NewAppHandler(logCtx, r, app, appParser)
|
||||
@@ -307,6 +307,11 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
|
||||
}))
|
||||
defer subCtx.Commit("finish gc resourceTrackers")
|
||||
|
||||
statusUpdater := r.updateStatus
|
||||
if isPatch {
|
||||
statusUpdater = r.patchStatus
|
||||
}
|
||||
|
||||
var options []resourcekeeper.GCOption
|
||||
if !gcOutdated {
|
||||
options = append(options, resourcekeeper.DisableMarkStageGCOption{}, resourcekeeper.DisableGCComponentRevisionOption{}, resourcekeeper.DisableLegacyGCOption{})
|
||||
@@ -314,8 +319,10 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
|
||||
finished, waiting, err := handler.resourceKeeper.GarbageCollect(logCtx, options...)
|
||||
if err != nil {
|
||||
logCtx.Error(err, "Failed to gc resourcetrackers")
|
||||
r.Recorder.Event(handler.app, event.Warning(velatypes.ReasonFailedGC, err))
|
||||
return r.endWithNegativeCondition(logCtx, handler.app, condition.ReconcileError(err), phase)
|
||||
cond := condition.Deleting()
|
||||
cond.Message = fmt.Sprintf("error encountered during garbage collection: %s", err.Error())
|
||||
handler.app.Status.SetConditions(cond)
|
||||
return r.result(statusUpdater(logCtx, handler.app, phase)).ret()
|
||||
}
|
||||
if !finished {
|
||||
logCtx.Info("GarbageCollecting resourcetrackers unfinished")
|
||||
@@ -324,13 +331,13 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
|
||||
cond.Message = fmt.Sprintf("Waiting for %s to delete. (At least %d resources are deleting.)", waiting[0].DisplayName(), len(waiting))
|
||||
}
|
||||
handler.app.Status.SetConditions(cond)
|
||||
return r.result(r.patchStatus(logCtx, handler.app, phase)).requeue(baseGCBackoffWaitTime).ret()
|
||||
return r.result(statusUpdater(logCtx, handler.app, phase)).requeue(baseGCBackoffWaitTime).ret()
|
||||
}
|
||||
logCtx.Info("GarbageCollected resourcetrackers")
|
||||
if !isPatch {
|
||||
return r.result(r.updateStatus(logCtx, handler.app, common.ApplicationRunningWorkflow)).ret()
|
||||
phase = common.ApplicationRunningWorkflow
|
||||
}
|
||||
return r.result(r.patchStatus(logCtx, handler.app, phase)).ret()
|
||||
return r.result(statusUpdater(logCtx, handler.app, phase)).ret()
|
||||
}
|
||||
|
||||
type reconcileResult struct {
|
||||
|
||||
@@ -43,17 +43,24 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// Reconciler reconciles a ComponentDefinition object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
defRevLimit int
|
||||
concurrentReconciles int
|
||||
ignoreDefNoCtrlReq bool
|
||||
controllerVersion string
|
||||
}
|
||||
|
||||
// Reconcile is the main logic for ComponentDefinition controller
|
||||
@@ -68,6 +75,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !r.matchControllerRequirement(&componentDefinition) {
|
||||
klog.InfoS("skip componentDefinition: not match the controller requirement of componentDefinition", "componentDefinition", klog.KObj(&componentDefinition))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// refresh package discover when componentDefinition is registered
|
||||
if componentDefinition.Spec.Workload.Type != types.AutoDetectWorkloadDefinition {
|
||||
err := utils.RefreshPackageDiscover(ctx, r.Client, r.dm, r.pd, &componentDefinition)
|
||||
@@ -187,12 +199,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Setup adds a controller that reconciles ComponentDefinition.
|
||||
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
|
||||
r := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
options: parseOptions(args),
|
||||
}
|
||||
return r.SetupWithManager(mgr)
|
||||
}
|
||||
|
||||
func parseOptions(args oamctrl.Args) options {
|
||||
return options{
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
|
||||
controllerVersion: version.VelaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) matchControllerRequirement(componentDefinition *v1beta1.ComponentDefinition) bool {
|
||||
if componentDefinition.Annotations != nil {
|
||||
if requireVersion, ok := componentDefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
|
||||
return requireVersion == r.controllerVersion
|
||||
}
|
||||
}
|
||||
if r.ignoreDefNoCtrlReq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r = Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
defRevLimit: defRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
options: options{
|
||||
defRevLimit: defRevisionLimit,
|
||||
},
|
||||
}
|
||||
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
|
||||
var ctx context.Context
|
||||
|
||||
@@ -42,17 +42,24 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// Reconciler reconciles a TraitDefinition object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
defRevLimit int
|
||||
concurrentReconciles int
|
||||
ignoreDefNoCtrlReq bool
|
||||
controllerVersion string
|
||||
}
|
||||
|
||||
// Reconcile is the main logic for TraitDefinition controller
|
||||
@@ -67,6 +74,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !r.matchControllerRequirement(&traitdefinition) {
|
||||
klog.InfoS("skip traitDefinition: not match the controller requirement of traitDefinition", "traitDefinition", klog.KObj(&traitdefinition))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// this is a placeholder for finalizer here in the future
|
||||
if traitdefinition.DeletionTimestamp != nil {
|
||||
klog.InfoS("The TraitDefinition is being deleted", "traitDefinition", klog.KRef(req.Namespace, req.Name))
|
||||
@@ -193,12 +205,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Setup adds a controller that reconciles TraitDefinition.
|
||||
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
|
||||
r := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
options: parseOptions(args),
|
||||
}
|
||||
return r.SetupWithManager(mgr)
|
||||
}
|
||||
|
||||
func parseOptions(args oamctrl.Args) options {
|
||||
return options{
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
|
||||
controllerVersion: version.VelaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) matchControllerRequirement(traitDefinition *v1beta1.TraitDefinition) bool {
|
||||
if traitDefinition.Annotations != nil {
|
||||
if requireVersion, ok := traitDefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
|
||||
return requireVersion == r.controllerVersion
|
||||
}
|
||||
}
|
||||
if r.ignoreDefNoCtrlReq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r = Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
defRevLimit: defRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
options: options{
|
||||
defRevLimit: defRevisionLimit,
|
||||
},
|
||||
}
|
||||
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
|
||||
var ctx context.Context
|
||||
|
||||
@@ -90,11 +90,13 @@ var _ = BeforeSuite(func(done Done) {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r = Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
defRevLimit: defRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: dm,
|
||||
pd: pd,
|
||||
options: options{
|
||||
defRevLimit: defRevisionLimit,
|
||||
},
|
||||
}
|
||||
Expect(r.SetupWithManager(mgr)).ToNot(HaveOccurred())
|
||||
var ctx context.Context
|
||||
|
||||
@@ -42,17 +42,24 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/version"
|
||||
)
|
||||
|
||||
// Reconciler reconciles a WorkflowStepDefinition object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
pd *packages.PackageDiscover
|
||||
Scheme *runtime.Scheme
|
||||
record event.Recorder
|
||||
options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
defRevLimit int
|
||||
concurrentReconciles int
|
||||
ignoreDefNoCtrlReq bool
|
||||
controllerVersion string
|
||||
}
|
||||
|
||||
// Reconcile is the main logic for WorkflowStepDefinition controller
|
||||
@@ -68,6 +75,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
if !r.matchControllerRequirement(&wfstepdefinition) {
|
||||
klog.InfoS("skip workflowStepDefinition: not match the controller requirement of workflowStepDefinition", "workflowStepDefinition", klog.KObj(&wfstepdefinition))
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// this is a placeholder for finalizer here in the future
|
||||
if wfstepdefinition.DeletionTimestamp != nil {
|
||||
return ctrl.Result{}, nil
|
||||
@@ -192,11 +204,32 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// Setup adds a controller that reconciles WorkflowStepDefinition.
|
||||
func Setup(mgr ctrl.Manager, args oamctrl.Args) error {
|
||||
r := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
dm: args.DiscoveryMapper,
|
||||
pd: args.PackageDiscover,
|
||||
options: parseOptions(args),
|
||||
}
|
||||
return r.SetupWithManager(mgr)
|
||||
}
|
||||
|
||||
func parseOptions(args oamctrl.Args) options {
|
||||
return options{
|
||||
defRevLimit: args.DefRevisionLimit,
|
||||
concurrentReconciles: args.ConcurrentReconciles,
|
||||
ignoreDefNoCtrlReq: args.IgnoreDefinitionWithoutControllerRequirement,
|
||||
controllerVersion: version.VelaVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reconciler) matchControllerRequirement(wfstepdefinition *v1beta1.WorkflowStepDefinition) bool {
|
||||
if wfstepdefinition.Annotations != nil {
|
||||
if requireVersion, ok := wfstepdefinition.Annotations[oam.AnnotationControllerRequirement]; ok {
|
||||
return requireVersion == r.controllerVersion
|
||||
}
|
||||
}
|
||||
if r.ignoreDefNoCtrlReq {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ const (
|
||||
// AnnotationWorkloadName indicates the managed workload's name by trait
|
||||
AnnotationWorkloadName = "trait.oam.dev/workload-name"
|
||||
|
||||
// AnnotationControllerRequirement indicates the controller version that can process the application.
|
||||
// AnnotationControllerRequirement indicates the controller version that can process the application/definition.
|
||||
AnnotationControllerRequirement = "app.oam.dev/controller-version-require"
|
||||
|
||||
// AnnotationApplicationServiceAccountName indicates the name of the ServiceAccount to use to apply Components and run Workflow.
|
||||
|
||||
@@ -81,3 +81,14 @@ func TestParseApplyOncePolicy(t *testing.T) {
|
||||
r.NoError(err)
|
||||
r.Equal(policySpec, spec)
|
||||
}
|
||||
|
||||
func TestParsePolicy(t *testing.T) {
|
||||
r := require.New(t)
|
||||
// Test skipping empty policy
|
||||
app := &v1beta1.Application{Spec: v1beta1.ApplicationSpec{
|
||||
Policies: []v1beta1.AppPolicy{{Type: "example", Name: "s", Properties: nil}},
|
||||
}}
|
||||
exists, err := parsePolicy(app, "example", nil)
|
||||
r.False(exists, "empty policy should not be included")
|
||||
r.NoError(err)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ const (
|
||||
// GetEnvBindingPolicy extract env-binding policy with given policy name, if policy name is empty, the first env-binding policy will be used
|
||||
func GetEnvBindingPolicy(app *v1beta1.Application, policyName string) (*v1alpha1.EnvBindingSpec, error) {
|
||||
for _, policy := range app.Spec.Policies {
|
||||
if (policy.Name == policyName || policyName == "") && policy.Type == v1alpha1.EnvBindingPolicyType {
|
||||
if (policy.Name == policyName || policyName == "") && policy.Type == v1alpha1.EnvBindingPolicyType && policy.Properties != nil {
|
||||
envBindingSpec := &v1alpha1.EnvBindingSpec{}
|
||||
err := json.Unmarshal(policy.Properties.Raw, envBindingSpec)
|
||||
return envBindingSpec, err
|
||||
|
||||
@@ -19,6 +19,7 @@ package policy
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
errors2 "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -32,6 +33,9 @@ import (
|
||||
|
||||
// ParseOverridePolicyRelatedDefinitions get definitions inside override policy
|
||||
func ParseOverridePolicyRelatedDefinitions(ctx context.Context, cli client.Client, app *v1beta1.Application, policy v1beta1.AppPolicy) (compDefs []*v1beta1.ComponentDefinition, traitDefs []*v1beta1.TraitDefinition, err error) {
|
||||
if policy.Properties == nil {
|
||||
return compDefs, traitDefs, fmt.Errorf("override policy %s must not have empty properties", policy.Name)
|
||||
}
|
||||
spec := &v1alpha1.OverridePolicySpec{}
|
||||
if err = json.Unmarshal(policy.Properties.Raw, spec); err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "invalid override policy spec")
|
||||
|
||||
@@ -62,6 +62,12 @@ func TestParseOverridePolicyRelatedDefinitions(t *testing.T) {
|
||||
Policy: v1beta1.AppPolicy{Properties: &runtime.RawExtension{Raw: []byte(`{"components":[{"type":"comp","traits":[{"type":"trait-404"}]}]}`)}},
|
||||
Error: "failed to get trait definition",
|
||||
},
|
||||
"empty-policy": {
|
||||
Policy: v1beta1.AppPolicy{Properties: nil},
|
||||
ComponentDefs: nil,
|
||||
TraitDefs: nil,
|
||||
Error: "have empty properties",
|
||||
},
|
||||
}
|
||||
for name, tt := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
@@ -65,6 +66,9 @@ func GetPlacementsFromTopologyPolicies(ctx context.Context, cli client.Client, a
|
||||
hasTopologyPolicy := false
|
||||
for _, policy := range policies {
|
||||
if policy.Type == v1alpha1.TopologyPolicyType {
|
||||
if policy.Properties == nil {
|
||||
return nil, fmt.Errorf("topology policy %s must not have empty properties", policy.Name)
|
||||
}
|
||||
hasTopologyPolicy = true
|
||||
topologySpec := &v1alpha1.TopologyPolicySpec{}
|
||||
if err := utils.StrictUnmarshal(policy.Properties.Raw, topologySpec); err != nil {
|
||||
|
||||
@@ -146,6 +146,10 @@ func TestGetClusterLabelSelectorInTopology(t *testing.T) {
|
||||
Inputs: []v1beta1.AppPolicy{},
|
||||
Outputs: []v1alpha1.PlacementDecision{{Cluster: "local", Namespace: ""}},
|
||||
},
|
||||
"empty-topology-policy": {
|
||||
Inputs: []v1beta1.AppPolicy{{Type: "topology", Name: "some-name", Properties: nil}},
|
||||
Error: "have empty properties",
|
||||
},
|
||||
}
|
||||
for name, tt := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
||||
@@ -437,7 +437,7 @@ func (h *gcHandler) GarbageCollectLegacyResourceTrackers(ctx context.Context) er
|
||||
}
|
||||
}
|
||||
for _, policy := range h.app.Spec.Policies {
|
||||
if policy.Type == v1alpha1.EnvBindingPolicyType {
|
||||
if policy.Type == v1alpha1.EnvBindingPolicyType && policy.Properties != nil {
|
||||
spec := &v1alpha1.EnvBindingSpec{}
|
||||
if err = json.Unmarshal(policy.Properties.Raw, &spec); err == nil {
|
||||
for _, env := range spec.Envs {
|
||||
|
||||
@@ -96,7 +96,9 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
|
||||
switch req.Operation {
|
||||
case admissionv1.Create:
|
||||
if allErrs := h.ValidateCreate(ctx, app); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, mergeErrors(allErrs))
|
||||
// http.StatusUnprocessableEntity will NOT report any error descriptions
|
||||
// to the client, use generic http.StatusBadRequest instead.
|
||||
return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs))
|
||||
}
|
||||
case admissionv1.Update:
|
||||
oldApp := &v1beta1.Application{}
|
||||
@@ -105,7 +107,7 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
|
||||
}
|
||||
if app.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
if allErrs := h.ValidateUpdate(ctx, app, oldApp); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, mergeErrors(allErrs))
|
||||
return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs))
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -373,4 +373,21 @@ var _ = Describe("Test Application Validator", func() {
|
||||
resp = handler.Handle(ctx, req)
|
||||
Expect(resp.Allowed).Should(BeFalse())
|
||||
})
|
||||
|
||||
It("Test Application with empty policy", func() {
|
||||
req := admission.Request{
|
||||
AdmissionRequest: admissionv1.AdmissionRequest{
|
||||
Operation: admissionv1.Create,
|
||||
Resource: metav1.GroupVersionResource{Group: "core.oam.dev", Version: "v1beta1", Resource: "applications"},
|
||||
Object: runtime.RawExtension{
|
||||
Raw: []byte(`
|
||||
{"kind":"Application","metadata":{"name":"app-with-empty-policy-webhook-test", "namespace":"default"},
|
||||
"spec":{"components":[],"policies":[{"name":"2345","type":"garbage-collect","properties":null}]}}
|
||||
`),
|
||||
},
|
||||
},
|
||||
}
|
||||
resp := handler.Handle(ctx, req)
|
||||
Expect(resp.Allowed).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -118,13 +118,14 @@ func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) a
|
||||
if err := h.Decoder.DecodeRaw(req.AdmissionRequest.OldObject, oldApp); err != nil {
|
||||
return admission.Errored(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
if allErrs := h.ValidateUpdate(ctx, app, oldApp); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, allErrs.ToAggregate())
|
||||
// http.StatusUnprocessableEntity will NOT report any error descriptions
|
||||
// to the client, use generic http.StatusBadRequest instead.
|
||||
return admission.Errored(http.StatusBadRequest, allErrs.ToAggregate())
|
||||
}
|
||||
case admissionv1.Create:
|
||||
if allErrs := h.ValidateCreate(ctx, app); len(allErrs) > 0 {
|
||||
return admission.Errored(http.StatusUnprocessableEntity, allErrs.ToAggregate())
|
||||
return admission.Errored(http.StatusBadRequest, allErrs.ToAggregate())
|
||||
}
|
||||
default:
|
||||
// Do nothing for CONNECT
|
||||
|
||||
@@ -134,7 +134,7 @@ func (h *provider) ApplyInParallel(ctx wfContext.Context, v *value.Value, act ty
|
||||
deployCtx := multicluster.ContextWithClusterName(context.Background(), cluster)
|
||||
deployCtx = auth.ContextWithUserInfo(deployCtx, h.app)
|
||||
if err = h.apply(deployCtx, cluster, common.WorkflowResourceCreator, workloads...); err != nil {
|
||||
return v.FillObject(err, "err")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -126,6 +126,9 @@ func overrideConfiguration(policies []v1beta1.AppPolicy, components []common.App
|
||||
var err error
|
||||
for _, policy := range policies {
|
||||
if policy.Type == v1alpha1.OverridePolicyType {
|
||||
if policy.Properties == nil {
|
||||
return nil, fmt.Errorf("override policy %s must not have empty properties", policy.Name)
|
||||
}
|
||||
overrideSpec := &v1alpha1.OverridePolicySpec{}
|
||||
if err := utils.StrictUnmarshal(policy.Properties.Raw, overrideSpec); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse override policy %s", policy.Name)
|
||||
|
||||
@@ -48,6 +48,14 @@ func TestOverrideConfiguration(t *testing.T) {
|
||||
}},
|
||||
Error: "failed to parse override policy",
|
||||
},
|
||||
"empty-policy": {
|
||||
Policies: []v1beta1.AppPolicy{{
|
||||
Name: "override-policy",
|
||||
Type: "override",
|
||||
Properties: nil,
|
||||
}},
|
||||
Error: "empty properties",
|
||||
},
|
||||
"normal": {
|
||||
Policies: []v1beta1.AppPolicy{{
|
||||
Name: "override-policy",
|
||||
|
||||
@@ -168,7 +168,9 @@ func (g *DeployPreApproveWorkflowStepGenerator) Generate(app *v1beta1.Applicatio
|
||||
for _, step := range existingSteps {
|
||||
if step.Type == "deploy" && !lastSuspend {
|
||||
props := DeployWorkflowStepSpec{}
|
||||
_ = utils.StrictUnmarshal(step.Properties.Raw, &props)
|
||||
if step.Properties != nil {
|
||||
_ = utils.StrictUnmarshal(step.Properties.Raw, &props)
|
||||
}
|
||||
if props.Auto != nil && !*props.Auto {
|
||||
steps = append(steps, v1beta1.WorkflowStep{
|
||||
Name: "manual-approve-" + step.Name,
|
||||
|
||||
@@ -403,14 +403,14 @@ func (w *workflow) setMetadataToContext(wfCtx wfContext.Context) error {
|
||||
return wfCtx.SetVar(metadata, wfTypes.ContextKeyMetadata)
|
||||
}
|
||||
|
||||
func (e *engine) getBackoffTimes(stepID string) (success bool, backoffTimes int) {
|
||||
func (e *engine) getBackoffTimes(stepID string) int {
|
||||
if v, ok := e.wfCtx.GetValueInMemory(wfTypes.ContextPrefixBackoffTimes, stepID); ok {
|
||||
times, ok := v.(int)
|
||||
if ok {
|
||||
return true, times
|
||||
return times
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
return -1
|
||||
}
|
||||
|
||||
func (e *engine) getBackoffWaitTime() int {
|
||||
@@ -418,17 +418,19 @@ func (e *engine) getBackoffWaitTime() int {
|
||||
minTimes := 15
|
||||
found := false
|
||||
for _, step := range e.status.Steps {
|
||||
success, backoffTimes := e.getBackoffTimes(step.ID)
|
||||
if success && backoffTimes < minTimes {
|
||||
minTimes = backoffTimes
|
||||
if backoffTimes := e.getBackoffTimes(step.ID); backoffTimes > 0 {
|
||||
found = true
|
||||
if backoffTimes < minTimes {
|
||||
minTimes = backoffTimes
|
||||
}
|
||||
}
|
||||
if step.SubStepsStatus != nil {
|
||||
for _, subStep := range step.SubStepsStatus {
|
||||
success, backoffTimes := e.getBackoffTimes(subStep.ID)
|
||||
if success && backoffTimes < minTimes {
|
||||
minTimes = backoffTimes
|
||||
if backoffTimes := e.getBackoffTimes(subStep.ID); backoffTimes > 0 {
|
||||
found = true
|
||||
if backoffTimes < minTimes {
|
||||
minTimes = backoffTimes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,10 +912,12 @@ var _ = Describe("Test Workflow", func() {
|
||||
Expect(interval).Should(BeEquivalentTo(int(0.05 * math.Pow(2, float64(i+5)))))
|
||||
}
|
||||
|
||||
_, err = wf.ExecuteSteps(ctx, revision, runners)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
interval = e.getBackoffWaitTime()
|
||||
Expect(interval).Should(BeEquivalentTo(MaxWorkflowWaitBackoffTime))
|
||||
for i := 0; i < 10; i++ {
|
||||
_, err = wf.ExecuteSteps(ctx, revision, runners)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
interval = e.getBackoffWaitTime()
|
||||
Expect(interval).Should(BeEquivalentTo(MaxWorkflowWaitBackoffTime))
|
||||
}
|
||||
|
||||
By("Test get backoff time after clean")
|
||||
wfContext.CleanupMemoryStore(app.Name, app.Namespace)
|
||||
|
||||
@@ -165,7 +165,7 @@ var _ = Describe("Addon status or info", func() {
|
||||
Expect(ds.DeleteRegistry(context.Background(), "KubeVela")).To(Succeed())
|
||||
})
|
||||
|
||||
It("should display addon name and disabled status, registry name, available versions, dependencies, and parameters(optional)", func() {
|
||||
PIt("should display addon name and disabled status, registry name, available versions, dependencies, and parameters(optional)", func() {
|
||||
addonName := "velaux"
|
||||
res, _, err := generateAddonInfo(k8sClient, addonName)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -285,7 +285,7 @@ func NewDefinitionInitCommand(c common.Args) *cobra.Command {
|
||||
cmd.Flags().StringP(FlagTemplateYAML, "f", "", "Specify the template yaml file that definition will use to build the schema. If empty, a default template for the given definition type will be used.")
|
||||
cmd.Flags().StringP(FlagOutput, "o", "", "Specify the output path of the generated definition. If empty, the definition will be printed in the console.")
|
||||
cmd.Flags().BoolP(FlagInteractive, "i", false, "Specify whether use interactive process to help generate definitions.")
|
||||
cmd.Flags().StringP(FlagProvider, "p", "", "Specify which provider the cloud resource definition belongs to. Only `alibaba`, `aws`, `azure` are supported.")
|
||||
cmd.Flags().StringP(FlagProvider, "p", "", "Specify which provider the cloud resource definition belongs to. Only `alibaba`, `aws`, `azure`, `gcp`, `baidu`, `tencent`, `elastic`, `ucloud`, `vsphere` are supported.")
|
||||
cmd.Flags().StringP(FlagGit, "", "", "Specify which git repository the configuration(HCL) is stored in. Valid when --provider/-p is set.")
|
||||
cmd.Flags().StringP(FlagLocal, "", "", "Specify the local path of the configuration(HCL) file. Valid when --provider/-p is set.")
|
||||
cmd.Flags().StringP(FlagPath, "", "", "Specify which path the configuration(HCL) is stored in the Git repository. Valid when --git is set.")
|
||||
@@ -298,7 +298,7 @@ func generateTerraformTypedComponentDefinition(cmd *cobra.Command, name, kind, p
|
||||
}
|
||||
|
||||
switch provider {
|
||||
case "aws", "azure", "alibaba", "tencent", "gcp", "baidu", "elastic", "ucloud":
|
||||
case "aws", "azure", "alibaba", "tencent", "gcp", "baidu", "elastic", "ucloud", "vsphere":
|
||||
var terraform *commontype.Terraform
|
||||
|
||||
git, err := cmd.Flags().GetString(FlagGit)
|
||||
@@ -374,7 +374,7 @@ func generateTerraformTypedComponentDefinition(cmd *cobra.Command, name, kind, p
|
||||
}
|
||||
return out.String(), nil
|
||||
default:
|
||||
return "", errors.Errorf("Provider `%s` is not supported. Only `alibaba`, `aws`, `azure`, `gcp`, `baidu`, `tencent`, `elastic`, `ucloud` are supported.", provider)
|
||||
return "", errors.Errorf("Provider `%s` is not supported. Only `alibaba`, `aws`, `azure`, `gcp`, `baidu`, `tencent`, `elastic`, `ucloud`, `vsphere` are supported.", provider)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
@@ -48,11 +49,15 @@ type UnInstallArgs struct {
|
||||
Namespace string
|
||||
Detail bool
|
||||
force bool
|
||||
cancel bool
|
||||
}
|
||||
|
||||
// NewUnInstallCommand creates `uninstall` command to uninstall vela core
|
||||
func NewUnInstallCommand(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command {
|
||||
unInstallArgs := &UnInstallArgs{Args: c, userInput: NewUserInput(), helmHelper: helm.NewHelper()}
|
||||
unInstallArgs := &UnInstallArgs{Args: c, userInput: &UserInput{
|
||||
Writer: ioStreams.Out,
|
||||
Reader: bufio.NewReader(ioStreams.In),
|
||||
}, helmHelper: helm.NewHelper()}
|
||||
cmd := &cobra.Command{
|
||||
Use: "uninstall",
|
||||
Short: "Uninstalls KubeVela from a Kubernetes cluster",
|
||||
@@ -60,8 +65,8 @@ func NewUnInstallCommand(c common.Args, order string, ioStreams util.IOStreams)
|
||||
Long: "Uninstalls KubeVela from a Kubernetes cluster.",
|
||||
Args: cobra.ExactArgs(0),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
userConfirmation := unInstallArgs.userInput.AskBool("Would you like to uninstall KubeVela from this cluster?", &UserInputOptions{AssumeYes: assumeYes})
|
||||
if !userConfirmation {
|
||||
unInstallArgs.cancel = unInstallArgs.userInput.AskBool("Would you like to uninstall KubeVela from this cluster?", &UserInputOptions{AssumeYes: assumeYes})
|
||||
if !unInstallArgs.cancel {
|
||||
return nil
|
||||
}
|
||||
kubeClient, err := c.GetClient()
|
||||
@@ -98,6 +103,9 @@ func NewUnInstallCommand(c common.Args, order string, ioStreams util.IOStreams)
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if !unInstallArgs.cancel {
|
||||
return nil
|
||||
}
|
||||
ioStreams.Info("Starting to uninstall KubeVela")
|
||||
restConfig, err := c.GetConfig()
|
||||
if err != nil {
|
||||
|
||||
@@ -19,14 +19,20 @@ package cli
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
pkgutils "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Test Install Command", func() {
|
||||
@@ -63,6 +69,17 @@ var _ = Describe("Test Install Command", func() {
|
||||
})
|
||||
})
|
||||
|
||||
func TestUninstall(t *testing.T) {
|
||||
// Test answering NO when prompted. Should just exit.
|
||||
cmd := NewUnInstallCommand(common.Args{}, "", pkgutils.IOStreams{
|
||||
Out: os.Stdout,
|
||||
In: strings.NewReader("n\n"),
|
||||
})
|
||||
cmd.SetArgs([]string{})
|
||||
err := cmd.Execute()
|
||||
assert.Nil(t, err, "should just exit if answer is no")
|
||||
}
|
||||
|
||||
var fluxcdYaml = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
|
||||
@@ -233,14 +233,17 @@ func prepareToForceDeleteTerraformComponents(ctx context.Context, k8sClient clie
|
||||
for _, c := range app.Spec.Components {
|
||||
var def corev1beta1.ComponentDefinition
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: c.Type, Namespace: types.DefaultKubeVelaNS}, &def); err != nil {
|
||||
return err
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: c.Type, Namespace: namespace}, &def); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if def.Spec.Schematic != nil && def.Spec.Schematic.Terraform != nil {
|
||||
var conf terraformapi.Configuration
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: c.Name, Namespace: namespace}, &conf); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
conf.Spec.ForceDelete = &forceDelete
|
||||
if err := k8sClient.Update(ctx, &conf); err != nil {
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta1"
|
||||
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -55,7 +55,7 @@ func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
|
||||
def1 := &v1beta1.ComponentDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ComponentDefinition",
|
||||
APIVersion: "core.oam.dev/v1beta1",
|
||||
APIVersion: "core.oam.dev/v1beta2",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "d1",
|
||||
@@ -75,6 +75,16 @@ func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
|
||||
userNamespace := "another-namespace"
|
||||
def2 := def1.DeepCopy()
|
||||
def2.SetNamespace(userNamespace)
|
||||
app2 := app1.DeepCopy()
|
||||
app2.SetNamespace(userNamespace)
|
||||
app2.SetName("app2")
|
||||
conf2 := conf1.DeepCopy()
|
||||
conf2.SetNamespace(userNamespace)
|
||||
|
||||
k8sClient1 := fake.NewClientBuilder().WithScheme(s).WithObjects(app1, def1, conf1).Build()
|
||||
|
||||
k8sClient2 := fake.NewClientBuilder().Build()
|
||||
@@ -83,6 +93,7 @@ func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
|
||||
|
||||
k8sClient4 := fake.NewClientBuilder().WithScheme(s).WithObjects(app1, def1).Build()
|
||||
|
||||
k8sClient5 := fake.NewClientBuilder().WithScheme(s).WithObjects(app2, def2, conf2).Build()
|
||||
type args struct {
|
||||
k8sClient client.Client
|
||||
namespace string
|
||||
@@ -141,16 +152,27 @@ func TestPrepareToForceDeleteTerraformComponents(t *testing.T) {
|
||||
"app1",
|
||||
},
|
||||
want: want{
|
||||
errMsg: "no kind is registered for the type",
|
||||
errMsg: "configurations.terraform.core.oam.dev \"c1\" not found",
|
||||
},
|
||||
},
|
||||
"can read definition from application namespace": {
|
||||
args: args{
|
||||
k8sClient5,
|
||||
userNamespace,
|
||||
"app2",
|
||||
},
|
||||
want: want{},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
err := prepareToForceDeleteTerraformComponents(ctx, tc.args.k8sClient, tc.args.namespace, tc.args.name)
|
||||
if err != nil || tc.want.errMsg != "" {
|
||||
if err != nil {
|
||||
assert.NotEmpty(t, tc.want.errMsg)
|
||||
assert.Contains(t, err.Error(), tc.want.errMsg)
|
||||
} else {
|
||||
assert.Empty(t, tc.want.errMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
kubevelatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
|
||||
@@ -501,5 +502,95 @@ var _ = Describe("Test multicluster scenario", func() {
|
||||
g.Expect(kerrors.IsNotFound(err)).Should(BeTrue())
|
||||
}, 2*time.Minute).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test application with failed gc and restart workflow", func() {
|
||||
By("duplicate cluster")
|
||||
secret := &corev1.Secret{}
|
||||
const secretName = "disconnection-test"
|
||||
Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: WorkerClusterName}, secret)).Should(Succeed())
|
||||
secret.SetName(secretName)
|
||||
secret.SetResourceVersion("")
|
||||
Expect(k8sClient.Create(hubCtx, secret)).Should(Succeed())
|
||||
defer func() {
|
||||
_ = k8sClient.Delete(hubCtx, secret)
|
||||
}()
|
||||
|
||||
By("create cluster normally")
|
||||
bs, err := os.ReadFile("./testdata/app/app-disconnection-test.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
app := &v1beta1.Application{}
|
||||
Expect(yaml.Unmarshal(bs, app)).Should(Succeed())
|
||||
app.SetNamespace(namespace)
|
||||
Expect(k8sClient.Create(hubCtx, app)).Should(Succeed())
|
||||
key := client.ObjectKeyFromObject(app)
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
|
||||
g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
|
||||
}).WithTimeout(30 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
|
||||
|
||||
By("disconnect cluster")
|
||||
Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: secretName}, secret)).Should(Succeed())
|
||||
secret.Data["tls.crt"] = []byte("-")
|
||||
Expect(k8sClient.Update(hubCtx, secret)).Should(Succeed())
|
||||
|
||||
By("update application")
|
||||
Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
|
||||
app.Spec.Policies = nil
|
||||
Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
|
||||
g.Expect(app.Status.ObservedGeneration).Should(Equal(app.Generation))
|
||||
g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
|
||||
rts := &v1beta1.ResourceTrackerList{}
|
||||
g.Expect(k8sClient.List(hubCtx, rts, client.MatchingLabels{oam.LabelAppName: key.Name, oam.LabelAppNamespace: key.Namespace})).Should(Succeed())
|
||||
cnt := 0
|
||||
for _, item := range rts.Items {
|
||||
if item.Spec.Type == v1beta1.ResourceTrackerTypeVersioned {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
g.Expect(cnt).Should(Equal(2))
|
||||
}).WithTimeout(30 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
|
||||
|
||||
By("try update application again")
|
||||
Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
|
||||
if app.Annotations == nil {
|
||||
app.Annotations = map[string]string{}
|
||||
}
|
||||
app.Annotations[oam.AnnotationPublishVersion] = "test"
|
||||
Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
|
||||
g.Expect(app.Status.LatestRevision).ShouldNot(BeNil())
|
||||
g.Expect(app.Status.LatestRevision.Revision).Should(Equal(int64(3)))
|
||||
g.Expect(app.Status.ObservedGeneration).Should(Equal(app.Generation))
|
||||
g.Expect(app.Status.Phase).Should(Equal(common.ApplicationRunning))
|
||||
}).WithTimeout(1 * time.Minute).WithPolling(2 * time.Second).Should(Succeed())
|
||||
|
||||
By("clear disconnection cluster secret")
|
||||
Expect(k8sClient.Get(hubCtx, types.NamespacedName{Namespace: kubevelatypes.DefaultKubeVelaNS, Name: secretName}, secret)).Should(Succeed())
|
||||
Expect(k8sClient.Delete(hubCtx, secret)).Should(Succeed())
|
||||
|
||||
By("update application again")
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Get(hubCtx, key, app)).Should(Succeed())
|
||||
app.Annotations[oam.AnnotationPublishVersion] = "test2"
|
||||
g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
|
||||
}).WithTimeout(10 * time.Second).WithPolling(2 * time.Second).Should(Succeed())
|
||||
|
||||
By("wait gc application completed")
|
||||
Eventually(func(g Gomega) {
|
||||
rts := &v1beta1.ResourceTrackerList{}
|
||||
g.Expect(k8sClient.List(hubCtx, rts, client.MatchingLabels{oam.LabelAppName: key.Name, oam.LabelAppNamespace: key.Namespace})).Should(Succeed())
|
||||
cnt := 0
|
||||
for _, item := range rts.Items {
|
||||
if item.Spec.Type == v1beta1.ResourceTrackerTypeVersioned {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
g.Expect(cnt).Should(Equal(1))
|
||||
}).WithTimeout(3 * time.Minute).WithPolling(10 * time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
17
test/e2e-multicluster-test/testdata/app/app-disconnection-test.yaml
vendored
Normal file
17
test/e2e-multicluster-test/testdata/app/app-disconnection-test.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: app-disconnection-test
|
||||
spec:
|
||||
components:
|
||||
- type: k8s-objects
|
||||
name: app-dis-cm
|
||||
properties:
|
||||
objects:
|
||||
- apiVersion: v1
|
||||
kind: ConfigMap
|
||||
policies:
|
||||
- type: topology
|
||||
name: disconnection-test
|
||||
properties:
|
||||
clusters: ["disconnection-test"]
|
||||
Reference in New Issue
Block a user