mirror of
https://github.com/kubevela/kubevela.git
synced 2026-03-02 01:30:47 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b24b52b651 | ||
|
|
ae91006a3d | ||
|
|
42d6791476 | ||
|
|
c85502e54a | ||
|
|
f9a6b22294 | ||
|
|
5085a99a12 | ||
|
|
18639ccbae | ||
|
|
f36c8f8fbb | ||
|
|
c55ac52c4d | ||
|
|
4d4ab9d098 | ||
|
|
14dfca44b4 | ||
|
|
44c6267b76 | ||
|
|
9c81aeed4a |
8
go.mod
8
go.mod
@@ -19,7 +19,7 @@ require (
|
||||
github.com/briandowns/spinner v1.11.1
|
||||
github.com/chartmuseum/helm-push v0.10.2
|
||||
github.com/cloudtty/cloudtty v0.2.0
|
||||
github.com/containerd/containerd v1.5.13
|
||||
github.com/containerd/containerd v1.5.16
|
||||
github.com/coreos/go-oidc v2.1.0+incompatible
|
||||
github.com/coreos/prometheus-operator v0.41.1
|
||||
github.com/crossplane/crossplane-runtime v0.14.1-0.20210722005935-0b469fcc77cd
|
||||
@@ -58,7 +58,7 @@ require (
|
||||
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
|
||||
github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2
|
||||
github.com/kubevela/prism v1.5.1-0.20220915071949-6bf3ad33f84f
|
||||
github.com/kubevela/workflow v0.3.3
|
||||
github.com/kubevela/workflow v0.3.4-0.20230103040208-bca032d481ed
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.1
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
|
||||
@@ -158,7 +158,7 @@ require (
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.5.5 // indirect
|
||||
github.com/cockroachdb/apd/v2 v2.0.2 // indirect
|
||||
github.com/containerd/continuity v0.1.0 // indirect
|
||||
github.com/containerd/continuity v0.3.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
@@ -230,7 +230,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
|
||||
14
go.sum
14
go.sum
@@ -448,16 +448,17 @@ github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoT
|
||||
github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
|
||||
github.com/containerd/containerd v1.5.2/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
|
||||
github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
|
||||
github.com/containerd/containerd v1.5.13 h1:XqvKw9i4P7/mFrC3TSM7yV5cwFZ9avXe6M3YANKnzEE=
|
||||
github.com/containerd/containerd v1.5.13/go.mod h1:3AlCrzKROjIuP3JALsY14n8YtntaUDBu7vek+rPN5Vc=
|
||||
github.com/containerd/containerd v1.5.16 h1:WsTS9tV0vQmRxkWAiiaoasHJ20jqVxVA15s93Bs4GIU=
|
||||
github.com/containerd/containerd v1.5.16/go.mod h1:bVZZA+0blg2Lw6+I4xDml7L3gum0LsFKe3TnFELlSFw=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
|
||||
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
|
||||
github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
|
||||
github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8=
|
||||
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
|
||||
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
||||
github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||
github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
|
||||
@@ -1336,8 +1337,8 @@ github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2 h1:C3cAfrxst1+dIWgLLh
|
||||
github.com/kubevela/pkg v0.0.0-20221024115939-a103acee6db2/go.mod h1:TgIGEB/r0NOy63Jzem7WsL3AIr34l+ClH9dmPqcZ4d4=
|
||||
github.com/kubevela/prism v1.5.1-0.20220915071949-6bf3ad33f84f h1:1lUtU1alPThdcsn4MI6XjPb7eJLuZPpmlEdgjtnUMKw=
|
||||
github.com/kubevela/prism v1.5.1-0.20220915071949-6bf3ad33f84f/go.mod h1:m724/7ANnB/iukyHW20+DicpeJMEC/JA0ZhgsHY10MA=
|
||||
github.com/kubevela/workflow v0.3.3 h1:NSbQGcABWJIzUV5wfWFJsrO/ffZ4mCVfofUtUHCTojQ=
|
||||
github.com/kubevela/workflow v0.3.3/go.mod h1:5jfZ8T1m/En44wDGRf2YqCSlODfEnAV+9PnzoLoDlFs=
|
||||
github.com/kubevela/workflow v0.3.4-0.20230103040208-bca032d481ed h1:VGeEvL/XOkwADhLS4Upz5zEM6Enjw4yx8JU3Z+UUqXw=
|
||||
github.com/kubevela/workflow v0.3.4-0.20230103040208-bca032d481ed/go.mod h1:5jfZ8T1m/En44wDGRf2YqCSlODfEnAV+9PnzoLoDlFs=
|
||||
github.com/kulti/thelper v0.4.0/go.mod h1:vMu2Cizjy/grP+jmsvOFDx1kYP6+PD1lqg4Yu5exl2U=
|
||||
github.com/kunwardeep/paralleltest v1.0.3/go.mod h1:vLydzomDFpk7yu5UX02RmP0H8QfRPOV/oFhWN85Mjb4=
|
||||
github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
@@ -1438,8 +1439,9 @@ github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxz
|
||||
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
|
||||
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
|
||||
github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0=
|
||||
|
||||
@@ -43,6 +43,9 @@ var (
|
||||
|
||||
// ErrBothCueAndYamlTmpl means yaml and cue app template are exist in addon
|
||||
ErrBothCueAndYamlTmpl = NewAddonError("yaml and cue app template are exist in addon, should only keep one of them")
|
||||
|
||||
// ErrFetch means fetch addon package error(package not exist or parse archive error and so on)
|
||||
ErrFetch = NewAddonError("cannot fetch addon package")
|
||||
)
|
||||
|
||||
// WrapErrRateLimit return ErrRateLimit if is the situation, or return error directly
|
||||
|
||||
@@ -280,8 +280,8 @@ func GetObservabilityAccessibilityInfo(ctx context.Context, k8sClient client.Cli
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
// FindWholeAddonPackagesFromRegistry find addons' WholeInstallPackage from registries, empty registryName indicates matching all
|
||||
func FindWholeAddonPackagesFromRegistry(ctx context.Context, k8sClient client.Client, addonNames []string, registryNames []string) ([]*WholeAddonPackage, error) {
|
||||
// FindAddonPackagesDetailFromRegistry find addons' WholeInstallPackage from registries, empty registryName indicates matching all
|
||||
func FindAddonPackagesDetailFromRegistry(ctx context.Context, k8sClient client.Client, addonNames []string, registryNames []string) ([]*WholeAddonPackage, error) {
|
||||
var addons []*WholeAddonPackage
|
||||
var registries []Registry
|
||||
|
||||
|
||||
@@ -29,21 +29,21 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
var _ = Describe("test FindAddonPackagesDetailFromRegistry", func() {
|
||||
Describe("when no registry is added, no matter what you do, it will just return error", func() {
|
||||
Context("when empty addonNames and registryNames is supplied", func() {
|
||||
It("should return error", func() {
|
||||
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{}, []string{})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("should return error", func() {
|
||||
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, nil, nil)
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, nil)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
Context("when non-empty addonNames and registryNames is supplied", func() {
|
||||
It("should return error saying ErrRegistryNotExist", func() {
|
||||
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"fluxcd"}, []string{"some-registry"})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"fluxcd"}, []string{"some-registry"})
|
||||
Expect(errors.Is(err, ErrRegistryNotExist)).To(BeTrue())
|
||||
})
|
||||
})
|
||||
@@ -70,18 +70,18 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
|
||||
Context("when empty addonNames and registryNames is supplied", func() {
|
||||
It("should return error, empty addonNames are not allowed", func() {
|
||||
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{}, []string{"KubeVela"})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{"KubeVela"})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("should return error, empty addonNames are not allowed", func() {
|
||||
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, nil, []string{"KubeVela"})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, []string{"KubeVela"})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("one existing addon name provided", func() {
|
||||
It("should return one valid result, matching all registries", func() {
|
||||
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"velaux"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux"}, nil)
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
@@ -89,7 +89,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
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"})
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux"}, []string{"KubeVela"})
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
@@ -100,7 +100,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
|
||||
Context("one non-existent addon name provided", func() {
|
||||
It("should return error as ErrNotExist", func() {
|
||||
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"non-existent-addon"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"non-existent-addon"}, nil)
|
||||
Expect(errors.Is(err, ErrNotExist)).To(BeTrue())
|
||||
Expect(res).To(BeNil())
|
||||
})
|
||||
@@ -108,7 +108,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
|
||||
Context("two existing addon names provided", func() {
|
||||
It("should return two valid result", func() {
|
||||
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"velaux", "traefik"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux", "traefik"}, nil)
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(2))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
@@ -122,7 +122,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
|
||||
Context("one existing addon name and one non-existent addon name provided", func() {
|
||||
It("should return only one valid result", func() {
|
||||
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"velaux", "non-existent-addon"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux", "non-existent-addon"}, nil)
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
@@ -151,25 +151,25 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
|
||||
Context("when empty addonNames and registryNames is supplied", func() {
|
||||
It("should return error, empty addonNames are not allowed", func() {
|
||||
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{}, []string{})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("should return error, empty addonNames are not allowed", func() {
|
||||
_, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, nil, []string{"testreg"})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, []string{"testreg"})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("one existing addon name provided", func() {
|
||||
It("should return one valid result, matching all registries", func() {
|
||||
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"example"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"example"}, nil)
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("example"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
})
|
||||
It("should return one valid result, matching one registry", func() {
|
||||
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"example"}, []string{"testreg"})
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"example"}, []string{"testreg"})
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("example"))
|
||||
@@ -179,7 +179,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
|
||||
Context("one non-existent addon name provided", func() {
|
||||
It("should return error as ErrNotExist", func() {
|
||||
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"non-existent-addon"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"non-existent-addon"}, nil)
|
||||
Expect(errors.Is(err, ErrNotExist)).To(BeTrue())
|
||||
Expect(res).To(BeNil())
|
||||
})
|
||||
@@ -187,7 +187,7 @@ var _ = Describe("test FindWholeAddonPackagesFromRegistry", func() {
|
||||
|
||||
Context("one existing addon name and one non-existent addon name provided", func() {
|
||||
It("should return only one valid result", func() {
|
||||
res, err := FindWholeAddonPackagesFromRegistry(context.Background(), k8sClient, []string{"example", "non-existent-addon"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"example", "non-existent-addon"}, nil)
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("example"))
|
||||
|
||||
@@ -174,7 +174,7 @@ func (i versionedRegistry) loadAddon(ctx context.Context, name, version string)
|
||||
addonPkg.Meta.SystemRequirements = LoadSystemRequirements(addonVersion.Annotations)
|
||||
return addonPkg, nil
|
||||
}
|
||||
return nil, fmt.Errorf("cannot load addon '%s'(%s) package from registry '%s'", addonVersion.Name, addonVersion.Version, i.name)
|
||||
return nil, ErrFetch
|
||||
}
|
||||
|
||||
// loadAddonVersions Load all available versions of the addon
|
||||
|
||||
@@ -1513,6 +1513,13 @@ func (c *applicationServiceImpl) DryRunAppOrRevision(ctx context.Context, appMod
|
||||
}
|
||||
case "REVISION":
|
||||
app, _, err = c.getAppModelFromRevision(ctx, appModel.Name, dryRunReq.Version)
|
||||
originalApp := &v1beta1.Application{}
|
||||
if err := c.KubeClient.Get(ctx, types.NamespacedName{
|
||||
Name: app.Name,
|
||||
Namespace: app.Namespace,
|
||||
}, originalApp); err == nil {
|
||||
app.ResourceVersion = originalApp.ResourceVersion
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -741,7 +741,6 @@ func pipelineStep2WorkflowStep(step model.WorkflowStep) v1alpha1.WorkflowStep {
|
||||
|
||||
func pipelineSpec2WorkflowSpec(spec model.WorkflowSpec) *v1alpha1.WorkflowSpec {
|
||||
res := &v1alpha1.WorkflowSpec{
|
||||
Mode: spec.Mode,
|
||||
Steps: make([]v1alpha1.WorkflowStep, 0),
|
||||
}
|
||||
for _, step := range spec.Steps {
|
||||
|
||||
@@ -302,6 +302,11 @@ func (r *Reconciler) gcResourceTrackers(logCtx monitorContext.Context, handler *
|
||||
}))
|
||||
defer subCtx.Commit("finish gc resourceTrackers")
|
||||
|
||||
statusUpdater := r.patchStatus
|
||||
if isUpdate {
|
||||
statusUpdater = r.updateStatus
|
||||
}
|
||||
|
||||
var options []resourcekeeper.GCOption
|
||||
if !gcOutdated {
|
||||
options = append(options, resourcekeeper.DisableMarkStageGCOption{}, resourcekeeper.DisableGCComponentRevisionOption{}, resourcekeeper.DisableLegacyGCOption{})
|
||||
@@ -309,8 +314,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")
|
||||
@@ -319,13 +326,10 @@ 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 isUpdate {
|
||||
return r.result(r.updateStatus(logCtx, handler.app, phase)).ret()
|
||||
}
|
||||
return r.result(r.patchStatus(logCtx, handler.app, phase)).ret()
|
||||
return r.result(statusUpdater(logCtx, handler.app, phase)).ret()
|
||||
}
|
||||
|
||||
type reconcileResult struct {
|
||||
|
||||
@@ -114,6 +114,7 @@ func HandleReplicas(ctx context.Context, rolloutComp string, c client.Client) as
|
||||
klog.InfoS("assemble force set workload replicas to 0", "Kind", u.GetKind(), "name", u.GetName())
|
||||
return nil
|
||||
}
|
||||
klog.Errorf("fail to get workload %s: %v", u.GetName(), err)
|
||||
return err
|
||||
}
|
||||
// the workload already exist, we cannot reset the replicas with manifest
|
||||
@@ -122,6 +123,7 @@ func HandleReplicas(ctx context.Context, rolloutComp string, c client.Client) as
|
||||
wlpv := fieldpath.Pave(workload.UnstructuredContent())
|
||||
replicas, err := wlpv.GetInteger(replicasFieldPath)
|
||||
if err != nil {
|
||||
klog.Errorf("fail to get `spec.replicas` field from workload %s: %v", u.GetName(), err)
|
||||
return err
|
||||
}
|
||||
if err = pv.SetNumber(replicasFieldPath, float64(replicas)); err != nil {
|
||||
|
||||
@@ -329,19 +329,6 @@ func RealtimePrintCommandOutput(cmd *exec.Cmd, logFile string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClusterObject2Map convert ClusterObjectReference to a readable map
|
||||
func ClusterObject2Map(refs []common.ClusterObjectReference) map[string]string {
|
||||
clusterResourceRefTmpl := "Cluster: %s | Namespace: %s | Kind: %s | Name: %s"
|
||||
objs := make(map[string]string, len(refs))
|
||||
for _, r := range refs {
|
||||
if r.Cluster == "" {
|
||||
r.Cluster = "local"
|
||||
}
|
||||
objs[r.Cluster+"/"+r.Namespace+"/"+r.Name+"/"+r.Kind] = fmt.Sprintf(clusterResourceRefTmpl, r.Cluster, r.Namespace, r.Kind, r.Name)
|
||||
}
|
||||
return objs
|
||||
}
|
||||
|
||||
// ResourceLocation indicates the resource location
|
||||
type ResourceLocation struct {
|
||||
Cluster string
|
||||
|
||||
@@ -305,9 +305,11 @@ func isResourceInTargetCluster(opt FilterOption, resource common.ClusterObjectRe
|
||||
if opt.Cluster == "" && opt.ClusterNamespace == "" {
|
||||
return true
|
||||
}
|
||||
if (opt.Cluster == resource.Cluster || (opt.Cluster == "local" && resource.Cluster == "")) && opt.ClusterNamespace == resource.ObjectReference.Namespace {
|
||||
if (opt.Cluster == resource.Cluster || (opt.Cluster == "local" && resource.Cluster == "")) &&
|
||||
(opt.ClusterNamespace == resource.ObjectReference.Namespace || opt.ClusterNamespace == "") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
monitorContext "github.com/kubevela/pkg/monitor/context"
|
||||
pkgmulticluster "github.com/kubevela/pkg/multicluster"
|
||||
wfContext "github.com/kubevela/workflow/pkg/context"
|
||||
"github.com/kubevela/workflow/pkg/cue/model/value"
|
||||
"github.com/kubevela/workflow/pkg/types"
|
||||
@@ -229,6 +230,7 @@ func (h *provider) CollectLogsInPod(ctx monitorContext.Context, wfCtx wfContext.
|
||||
return errors.Wrapf(err, "invalid log options content")
|
||||
}
|
||||
cliCtx := multicluster.ContextWithClusterName(ctx, cluster)
|
||||
h.cfg.Wrap(pkgmulticluster.NewTransportWrapper())
|
||||
clientSet, err := kubernetes.NewForConfig(h.cfg)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create kubernetes client")
|
||||
|
||||
@@ -70,18 +70,16 @@ const (
|
||||
|
||||
var enabledAddonColor = color.New(color.Bold, color.FgGreen)
|
||||
|
||||
var forceDisable bool
|
||||
var addonVersion string
|
||||
|
||||
var addonClusters string
|
||||
|
||||
var verboseStatus bool
|
||||
|
||||
var skipValidate bool
|
||||
|
||||
var overrideDefs bool
|
||||
|
||||
var dryRun bool
|
||||
var (
|
||||
forceDisable bool
|
||||
addonVersion string
|
||||
addonClusters string
|
||||
verboseStatus bool
|
||||
skipValidate bool
|
||||
overrideDefs bool
|
||||
dryRun bool
|
||||
yes2all bool
|
||||
)
|
||||
|
||||
// NewAddonCommand create `addon` command
|
||||
func NewAddonCommand(c common.Args, order string, ioStreams cmdutil.IOStreams) *cobra.Command {
|
||||
@@ -189,15 +187,24 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
return errors.Wrapf(err, "directory %s is invalid", addonOrDir)
|
||||
}
|
||||
name = filepath.Base(abs)
|
||||
if !yes2all {
|
||||
if err := checkUninstallFromClusters(ctx, k8sClient, name, addonArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = enableAddonByLocal(ctx, name, addonOrDir, k8sClient, dc, config, addonArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if filepath.IsAbs(addonOrDir) || strings.HasPrefix(addonOrDir, ".") || strings.HasSuffix(addonOrDir, "/") {
|
||||
return fmt.Errorf("addon directory %s not found in local", addonOrDir)
|
||||
return fmt.Errorf("addon directory %s not found in local file system", addonOrDir)
|
||||
}
|
||||
if !yes2all {
|
||||
if err := checkUninstallFromClusters(ctx, k8sClient, name, addonArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = enableAddon(ctx, k8sClient, dc, config, name, addonVersion, addonArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -217,6 +224,7 @@ func NewAddonEnableCommand(c common.Args, ioStream cmdutil.IOStreams) *cobra.Com
|
||||
cmd.Flags().BoolVarP(&skipValidate, "skip-version-validating", "s", false, "skip validating system version requirement")
|
||||
cmd.Flags().BoolVarP(&overrideDefs, "override-definitions", "", false, "override existing definitions if conflict with those contained in this addon")
|
||||
cmd.Flags().BoolVarP(&dryRun, FlagDryRun, "", false, "render all yaml files out without real execute it")
|
||||
cmd.Flags().BoolVarP(&yes2all, "yes", "y", false, "all checks will be skipped and the default answer is yes for all validation check.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -567,7 +575,7 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
|
||||
continue
|
||||
}
|
||||
err = pkgaddon.EnableAddon(ctx, addonName, version, k8sClient, dc, apply.NewAPIApplicator(k8sClient), config, registry, args, nil, pkgaddon.FilterDependencyRegistries(i, registries), opts...)
|
||||
if errors.Is(err, pkgaddon.ErrNotExist) {
|
||||
if errors.Is(err, pkgaddon.ErrNotExist) || errors.Is(err, pkgaddon.ErrFetch) {
|
||||
continue
|
||||
}
|
||||
if unMatchErr := new(pkgaddon.VersionUnMatchError); errors.As(err, unMatchErr) {
|
||||
@@ -600,10 +608,10 @@ func enableAddon(ctx context.Context, k8sClient client.Client, dc *discovery.Dis
|
||||
|
||||
func addonOptions() []pkgaddon.InstallOption {
|
||||
var opts []pkgaddon.InstallOption
|
||||
if skipValidate {
|
||||
if skipValidate || yes2all {
|
||||
opts = append(opts, pkgaddon.SkipValidateVersion)
|
||||
}
|
||||
if overrideDefs {
|
||||
if overrideDefs || yes2all {
|
||||
opts = append(opts, pkgaddon.OverrideDefinitions)
|
||||
}
|
||||
if dryRun {
|
||||
@@ -654,6 +662,16 @@ func statusAddon(name string, ioStreams cmdutil.IOStreams, cmd *cobra.Command, c
|
||||
return nil
|
||||
}
|
||||
|
||||
func addonNotExist(err error) bool {
|
||||
if errors.Is(err, pkgaddon.ErrNotExist) || errors.Is(err, pkgaddon.ErrRegistryNotExist) {
|
||||
return true
|
||||
}
|
||||
if strings.Contains(err.Error(), "not found") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// generateAddonInfo will get addon status, description, version, dependencies (and whether they are installed),
|
||||
// and parameters (and their current values).
|
||||
// The first return value is the formatted string for printing.
|
||||
@@ -664,12 +682,21 @@ func generateAddonInfo(c client.Client, name string) (string, pkgaddon.Status, e
|
||||
var installed bool
|
||||
var addonPackage *pkgaddon.WholeAddonPackage
|
||||
|
||||
// Check current addon status
|
||||
status, err := pkgaddon.GetAddonStatus(context.Background(), c, name)
|
||||
if err != nil {
|
||||
return res, status, err
|
||||
}
|
||||
|
||||
// Get addon install package
|
||||
if verboseStatus {
|
||||
if verboseStatus || status.AddonPhase == statusDisabled {
|
||||
// We need the metadata to get descriptions about parameters
|
||||
addonPackages, err := pkgaddon.FindWholeAddonPackagesFromRegistry(context.Background(), c, []string{name}, nil)
|
||||
// Not found error can be ignored, because the user can define their own addon. Others can't.
|
||||
if err != nil && !errors.Is(err, pkgaddon.ErrNotExist) && !errors.Is(err, pkgaddon.ErrRegistryNotExist) {
|
||||
addonPackages, err := pkgaddon.FindAddonPackagesDetailFromRegistry(context.Background(), c, []string{name}, nil)
|
||||
// If the state of addon is not disabled, we don't check the error, because it could be installed from local.
|
||||
if status.AddonPhase == statusDisabled && err != nil {
|
||||
if addonNotExist(err) {
|
||||
return "", pkgaddon.Status{}, fmt.Errorf("addon '%s' not found in cluster or any registry", name)
|
||||
}
|
||||
return "", pkgaddon.Status{}, err
|
||||
}
|
||||
if len(addonPackages) != 0 {
|
||||
@@ -677,12 +704,6 @@ func generateAddonInfo(c client.Client, name string) (string, pkgaddon.Status, e
|
||||
}
|
||||
}
|
||||
|
||||
// Check current addon status
|
||||
status, err := pkgaddon.GetAddonStatus(context.Background(), c, name)
|
||||
if err != nil {
|
||||
return res, status, err
|
||||
}
|
||||
|
||||
switch status.AddonPhase {
|
||||
case statusEnabled:
|
||||
installed = true
|
||||
@@ -785,12 +806,23 @@ func generateParameterString(status pkgaddon.Status, addonPackage *pkgaddon.Whol
|
||||
if addonPackage.APISchema == nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
ret = printSchema(addonPackage.APISchema, status.Parameters, 0)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertInterface2StringList(l []interface{}) []string {
|
||||
var strl []string
|
||||
for _, s := range l {
|
||||
str, ok := s.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
strl = append(strl, str)
|
||||
}
|
||||
return strl
|
||||
}
|
||||
|
||||
// printSchema prints the parameters in an addon recursively to a string
|
||||
// Deeper the parameter is nested, more the indentations.
|
||||
func printSchema(ref *openapi3.Schema, currentParams map[string]interface{}, indent int) string {
|
||||
@@ -853,19 +885,25 @@ func printSchema(ref *openapi3.Schema, currentParams map[string]interface{}, ind
|
||||
// Show current value
|
||||
if currentValue != "" {
|
||||
ret += addedIndent
|
||||
ret += "\tcurrent: " + color.New(color.FgGreen).Sprintf("%s\n", currentValue)
|
||||
}
|
||||
// Show default value
|
||||
if defaultValue != "" {
|
||||
ret += addedIndent
|
||||
ret += "\tdefault: " + fmt.Sprintf("%#v\n", defaultValue)
|
||||
ret += "\tcurrent value: " + color.New(color.FgGreen).Sprintf("%s\n", currentValue)
|
||||
}
|
||||
|
||||
// Show required or not
|
||||
if required {
|
||||
ret += addedIndent
|
||||
ret += "\trequired: "
|
||||
ret += color.GreenString("✔\n")
|
||||
}
|
||||
// Show Enum options
|
||||
if len(propValue.Value.Enum) > 0 {
|
||||
ret += addedIndent
|
||||
ret += "\toptions: \"" + strings.Join(convertInterface2StringList(propValue.Value.Enum), "\", \"") + "\"\n"
|
||||
}
|
||||
// Show default value
|
||||
if defaultValue != "" && currentValue == "" {
|
||||
ret += addedIndent
|
||||
ret += "\tdefault: " + fmt.Sprintf("%#v\n", defaultValue)
|
||||
}
|
||||
|
||||
// Object type param, we will get inside the object.
|
||||
// To show what's inside nested objects.
|
||||
@@ -1171,3 +1209,51 @@ func splitSpecifyRegistry(name string) (string, string, error) {
|
||||
return "", "", fmt.Errorf("invalid addon name, you should specify name only <addonName> or with registry as prefix <registryName>/<addonName>")
|
||||
}
|
||||
}
|
||||
|
||||
func checkUninstallFromClusters(ctx context.Context, k8sClient client.Client, addonName string, args map[string]interface{}) error {
|
||||
status, err := pkgaddon.GetAddonStatus(ctx, k8sClient, addonName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check addon status: %w", err)
|
||||
}
|
||||
if status.AddonPhase == statusDisabled {
|
||||
return nil
|
||||
}
|
||||
if _, ok := args["clusters"]; !ok {
|
||||
return nil
|
||||
}
|
||||
cList, ok := args["clusters"].([]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("clusters parameter must be a list of string")
|
||||
}
|
||||
|
||||
clusters := map[string]struct{}{}
|
||||
for _, c := range cList {
|
||||
clusterName := c.(string)
|
||||
clusters[clusterName] = struct{}{}
|
||||
}
|
||||
var disableClusters, installedClusters []string
|
||||
for c := range status.Clusters {
|
||||
if _, ok := clusters[c]; !ok {
|
||||
disableClusters = append(disableClusters, c)
|
||||
}
|
||||
installedClusters = append(installedClusters, c)
|
||||
}
|
||||
fmt.Println(color.New(color.FgRed).Sprintf("'%s' addon was currently installed on clusters %s, but this operation will uninstall from these clusters: %s \n", addonName, generateClustersInfo(installedClusters), generateClustersInfo(disableClusters)))
|
||||
input := NewUserInput()
|
||||
if !input.AskBool("Do you want to continue?", &UserInputOptions{AssumeYes: false}) {
|
||||
return fmt.Errorf("operation abort")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateClustersInfo(clusters []string) string {
|
||||
ret := "["
|
||||
for i, cluster := range clusters {
|
||||
ret += cluster
|
||||
if i < len(clusters)-1 {
|
||||
ret += ","
|
||||
}
|
||||
}
|
||||
ret += "]"
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ var _ = Describe("Addon status or info", func() {
|
||||
BeforeEach(func() {
|
||||
// Delete KubeVela registry
|
||||
ds := pkgaddon.NewRegistryDataStore(k8sClient)
|
||||
Expect(ds.DeleteRegistry(context.Background(), "KubeVela")).To(Succeed())
|
||||
Expect(ds.DeleteRegistry(context.Background(), "KubeVela")).Should(SatisfyAny(Succeed(), util.NotFoundMatcher{}))
|
||||
// Install fluxcd locally
|
||||
Expect(k8sClient.Create(context.Background(), &fluxcd)).Should(SatisfyAny(BeNil(), util.AlreadyExistMatcher{}))
|
||||
})
|
||||
@@ -287,6 +287,7 @@ var _ = Describe("Addon status or info", func() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(addonName, res, err)
|
||||
// Should include enabled status, like:
|
||||
// fluxcd: enabled (1.1.0)
|
||||
if !strings.Contains(res,
|
||||
@@ -349,6 +350,11 @@ var _ = Describe("Addon status or info", func() {
|
||||
"KubeVela",
|
||||
))
|
||||
})
|
||||
It("should report addon not exist in any registry name", func() {
|
||||
addonName := "not-exist"
|
||||
_, _, err := generateAddonInfo(k8sClient, addonName)
|
||||
Expect(err.Error()).Should(BeEquivalentTo("addon 'not-exist' not found in cluster or any registry"))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -19,7 +19,6 @@ package cli
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
@@ -27,7 +26,7 @@ import (
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"gotest.tools/assert"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
pkgaddon "github.com/oam-dev/kubevela/pkg/addon"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
@@ -91,8 +90,8 @@ func TestParseMap(t *testing.T) {
|
||||
for _, s := range testcase {
|
||||
r, err := parseAddonArgsToMap(s.args)
|
||||
if s.nilError {
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, s.res, r)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, s.res, r)
|
||||
} else {
|
||||
assert.Error(t, err, fmt.Sprintf("%v should be error case", s.args))
|
||||
}
|
||||
@@ -208,7 +207,7 @@ func TestTransCluster(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, s := range testcase {
|
||||
assert.DeepEqual(t, transClusters(s.str), s.res)
|
||||
assert.Equal(t, transClusters(s.str), s.res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,7 +340,7 @@ func TestPackageValidAddon(t *testing.T) {
|
||||
cmd := NewAddonPackageCommand(commandArgs)
|
||||
cmd.SetArgs([]string{"./test-data/addon/sample"})
|
||||
err := cmd.Execute()
|
||||
assert.NilError(t, err)
|
||||
assert.NoError(t, err)
|
||||
defer func() {
|
||||
_ = os.RemoveAll("sample-1.0.1.tgz")
|
||||
}()
|
||||
@@ -380,13 +379,13 @@ func TestGenerateParameterString(t *testing.T) {
|
||||
"dbURL": &openapi3.SchemaRef{
|
||||
Value: &openapi3.Schema{
|
||||
Description: "Specify the MongoDB URL. it only enabled where DB type is MongoDB.",
|
||||
Default: nil,
|
||||
Default: "abc.com",
|
||||
},
|
||||
},
|
||||
"dbType": &openapi3.SchemaRef{
|
||||
Value: &openapi3.Schema{
|
||||
Description: "Specify the database type, current support KubeAPI(default) and MongoDB.",
|
||||
Default: "kubeapi",
|
||||
Enum: []interface{}{"kubeapi", "mongodb"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -397,18 +396,19 @@ func TestGenerateParameterString(t *testing.T) {
|
||||
color.New(color.FgCyan).Sprintf("-> ") +
|
||||
color.New(color.Bold).Sprint("dbType") + ": " +
|
||||
"Specify the database type, current support KubeAPI(default) and MongoDB.\n" +
|
||||
"\tcurrent: " + color.New(color.FgGreen).Sprint("\"kubeapi\"\n") +
|
||||
"\tdefault: " + "\"kubeapi\"\n" +
|
||||
"\trequired: " + color.GreenString("✔\n"),
|
||||
"\tcurrent value: " + color.New(color.FgGreen).Sprint("\"kubeapi\"\n") +
|
||||
"\trequired: " + color.GreenString("✔\n") +
|
||||
"\toptions: \"kubeapi\", \"mongodb\"\n",
|
||||
// dbURL
|
||||
color.New(color.FgCyan).Sprintf("-> ") +
|
||||
color.New(color.Bold).Sprint("dbURL") + ": " +
|
||||
"Specify the MongoDB URL. it only enabled where DB type is MongoDB.",
|
||||
"Specify the MongoDB URL. it only enabled where DB type is MongoDB.\n" +
|
||||
"\tdefault: " + "\"abc.com\"\n",
|
||||
// database
|
||||
color.New(color.FgCyan).Sprintf("-> ") +
|
||||
color.New(color.Bold).Sprint("database") + ": " +
|
||||
"Specify the database name, for the kubeapi db type, it represents namespace.\n" +
|
||||
"\tcurrent: " + color.New(color.FgGreen).Sprint("\"kubevela\""),
|
||||
"\tcurrent value: " + color.New(color.FgGreen).Sprint("\"kubevela\""),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -416,7 +416,7 @@ func TestGenerateParameterString(t *testing.T) {
|
||||
for _, s := range testcase {
|
||||
res := generateParameterString(s.status, s.addonPackage)
|
||||
for _, o := range s.outputs {
|
||||
assert.Check(t, strings.Contains(res, o))
|
||||
assert.Contains(t, res, o)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -434,12 +434,12 @@ func TestNewAddonCreateCommand(t *testing.T) {
|
||||
|
||||
cmd.SetArgs([]string{"test-addon", "--chart", "a", "--helm-repo", "https://some.com", "--chart-version", "c"})
|
||||
err = cmd.Execute()
|
||||
assert.NilError(t, err)
|
||||
assert.NoError(t, err)
|
||||
_ = os.RemoveAll("test-addon")
|
||||
|
||||
cmd.SetArgs([]string{"test-addon"})
|
||||
err = cmd.Execute()
|
||||
assert.NilError(t, err)
|
||||
assert.NoError(t, err)
|
||||
_ = os.RemoveAll("test-addon")
|
||||
|
||||
}
|
||||
|
||||
@@ -99,6 +99,15 @@ func buildApplicationListTable(ctx context.Context, c client.Reader, namespace s
|
||||
service[s.Name] = s
|
||||
}
|
||||
|
||||
if len(a.Spec.Components) == 0 {
|
||||
if AllNamespace {
|
||||
table.AddRow(a.Namespace, a.Name, "", "", "", a.Status.Phase, "", "", a.CreationTimestamp)
|
||||
} else {
|
||||
table.AddRow(a.Name, "", "", "", a.Status.Phase, "", "", a.CreationTimestamp)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for idx, cmp := range a.Spec.Components {
|
||||
var appName = a.Name
|
||||
if idx > 0 {
|
||||
|
||||
@@ -95,7 +95,6 @@ func LoadPodDetail(cfg *rest.Config, pod *v1.Pod) Pod {
|
||||
podInfo := Pod{
|
||||
Name: pod.Name,
|
||||
Namespace: pod.Namespace,
|
||||
Cluster: pod.ClusterName,
|
||||
Ready: readyContainerNum(pod),
|
||||
Status: string(pod.Status.Phase),
|
||||
Age: utils.TimeFormat(time.Since(pod.CreationTimestamp.Time)),
|
||||
|
||||
@@ -256,13 +256,7 @@ func GetServiceEndpoints(ctx context.Context, appName string, namespace string,
|
||||
"appName": appName,
|
||||
"appNs": namespace,
|
||||
}
|
||||
if f.Component != "" {
|
||||
params["name"] = f.Component
|
||||
}
|
||||
if f.Cluster != "" && f.ClusterNamespace != "" {
|
||||
params["cluster"] = f.Cluster
|
||||
params["clusterNs"] = f.ClusterNamespace
|
||||
}
|
||||
setFilterParams(f, params)
|
||||
|
||||
velaQL := MakeVelaQL("service-endpoints-view", params, "status")
|
||||
queryView, err := velaql.ParseVelaQL(velaQL)
|
||||
@@ -292,13 +286,7 @@ func GetApplicationPods(ctx context.Context, appName string, namespace string, v
|
||||
"appName": appName,
|
||||
"appNs": namespace,
|
||||
}
|
||||
if f.Component != "" {
|
||||
params["name"] = f.Component
|
||||
}
|
||||
if f.Cluster != "" && f.ClusterNamespace != "" {
|
||||
params["cluster"] = f.Cluster
|
||||
params["clusterNs"] = f.ClusterNamespace
|
||||
}
|
||||
setFilterParams(f, params)
|
||||
|
||||
velaQL := MakeVelaQL("component-pod-view", params, "status")
|
||||
queryView, err := velaql.ParseVelaQL(velaQL)
|
||||
@@ -328,13 +316,7 @@ func GetApplicationServices(ctx context.Context, appName string, namespace strin
|
||||
"appName": appName,
|
||||
"appNs": namespace,
|
||||
}
|
||||
if f.Component != "" {
|
||||
params["name"] = f.Component
|
||||
}
|
||||
if f.Cluster != "" && f.ClusterNamespace != "" {
|
||||
params["cluster"] = f.Cluster
|
||||
params["clusterNs"] = f.ClusterNamespace
|
||||
}
|
||||
setFilterParams(f, params)
|
||||
velaQL := MakeVelaQL("component-service-view", params, "status")
|
||||
queryView, err := velaql.ParseVelaQL(velaQL)
|
||||
if err != nil {
|
||||
@@ -357,6 +339,20 @@ func GetApplicationServices(ctx context.Context, appName string, namespace strin
|
||||
return response.Services, nil
|
||||
}
|
||||
|
||||
// setFilterParams will convert Filter fields to velaQL params
|
||||
func setFilterParams(f Filter, params map[string]string) {
|
||||
if f.Component != "" {
|
||||
params["name"] = f.Component
|
||||
}
|
||||
if f.Cluster != "" {
|
||||
params["cluster"] = f.Cluster
|
||||
}
|
||||
if f.ClusterNamespace != "" {
|
||||
params["clusterNs"] = f.ClusterNamespace
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// QueryValue get queryValue from velaQL
|
||||
func QueryValue(ctx context.Context, velaC common.Args, queryView *velaql.QueryView) (*value.Value, error) {
|
||||
dm, err := velaC.GetDiscoveryMapper()
|
||||
|
||||
@@ -28,4 +28,30 @@ spec:
|
||||
env:
|
||||
key_for_nginx_first: value_first
|
||||
key_for_nginx_second: value_second
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: busybox
|
||||
spec:
|
||||
components:
|
||||
- name: busybox
|
||||
type: webservice
|
||||
properties:
|
||||
image: busybox
|
||||
cmd: ["sleep", "86400"]
|
||||
traits:
|
||||
- type: sidecar
|
||||
properties:
|
||||
name: sidecar-nginx
|
||||
image: nginx
|
||||
- type: env
|
||||
properties:
|
||||
# you can use env to control one container, if containerName not specified, it will patch on the first index container
|
||||
containerName: busybox
|
||||
env:
|
||||
key_for_busybox_first: value_first
|
||||
key_for_busybox_second: value_second
|
||||
```
|
||||
@@ -24,19 +24,15 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/kubevela/workflow/pkg/cue/packages"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/kubevela/workflow/pkg/cue/packages"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/cue"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
|
||||
@@ -156,8 +156,66 @@ func (ref *ParseReference) prepareConsoleParameter(tableName string, parameterLi
|
||||
return ConsoleReference{TableName: tableName, TableObject: table}
|
||||
}
|
||||
|
||||
// parseParameters parses every parameter
|
||||
// TODO(wonderflowe2e/plugin/plugin_test.go:122): refactor the code to reduce the complexity
|
||||
func cueValue2Ident(val cue.Value) *ast.Ident {
|
||||
var ident *ast.Ident
|
||||
if source, ok := val.Source().(*ast.Ident); ok {
|
||||
ident = source
|
||||
}
|
||||
if source, ok := val.Source().(*ast.Field); ok {
|
||||
if v, ok := source.Value.(*ast.Ident); ok {
|
||||
ident = v
|
||||
}
|
||||
}
|
||||
return ident
|
||||
}
|
||||
|
||||
func getIndentName(val cue.Value) string {
|
||||
ident := cueValue2Ident(val)
|
||||
if ident != nil && len(ident.Name) != 0 {
|
||||
return strings.TrimPrefix(ident.Name, "#")
|
||||
}
|
||||
return val.IncompleteKind().String()
|
||||
}
|
||||
|
||||
func getConcreteOrValueType(val cue.Value) string {
|
||||
op, elements := val.Expr()
|
||||
if op != cue.OrOp {
|
||||
return val.IncompleteKind().String()
|
||||
}
|
||||
var printTypes []string
|
||||
for _, ev := range elements {
|
||||
incompleteKind := ev.IncompleteKind().String()
|
||||
if !ev.IsConcrete() {
|
||||
return incompleteKind
|
||||
}
|
||||
ident := cueValue2Ident(ev)
|
||||
if ident != nil && len(ident.Name) != 0 {
|
||||
printTypes = append(printTypes, strings.TrimPrefix(ident.Name, "#"))
|
||||
} else {
|
||||
// only convert string in `or` operator for now
|
||||
opName, err := ev.String()
|
||||
if err != nil {
|
||||
return incompleteKind
|
||||
}
|
||||
opName = `"` + opName + `"`
|
||||
printTypes = append(printTypes, opName)
|
||||
}
|
||||
}
|
||||
return strings.Join(printTypes, " or ")
|
||||
}
|
||||
|
||||
func getSuffix(capName string, containSuffix bool) (string, string) {
|
||||
var suffixTitle = " (" + capName + ")"
|
||||
var suffixRef = "-" + strings.ToLower(capName)
|
||||
if !containSuffix || capName == "" {
|
||||
suffixTitle = ""
|
||||
suffixRef = ""
|
||||
}
|
||||
return suffixTitle, suffixRef
|
||||
}
|
||||
|
||||
// parseParameters parses every parameter to docs
|
||||
// TODO(wonderflow): refactor the code to reduce the complexity
|
||||
// nolint:staticcheck,gocyclo
|
||||
func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value, paramKey string, depth int, containSuffix bool) (string, []ConsoleReference, error) {
|
||||
var doc string
|
||||
@@ -167,19 +225,15 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
|
||||
if !paraValue.Exists() {
|
||||
return "", console, nil
|
||||
}
|
||||
suffixTitle, suffixRef := getSuffix(capName, containSuffix)
|
||||
|
||||
var suffixTitle = " (" + capName + ")"
|
||||
var suffixRef = "-" + strings.ToLower(capName)
|
||||
if !containSuffix || capName == "" {
|
||||
suffixTitle = ""
|
||||
suffixRef = ""
|
||||
}
|
||||
switch paraValue.Kind() {
|
||||
case cue.StructKind:
|
||||
arguments, err := paraValue.Struct()
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("arguments not defined as struct %w", err)
|
||||
return "", nil, fmt.Errorf("field %s not defined as struct %w", paramKey, err)
|
||||
}
|
||||
|
||||
if arguments.Len() == 0 {
|
||||
var param ReferenceParameter
|
||||
param.Name = "\\-"
|
||||
@@ -200,7 +254,7 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
|
||||
continue
|
||||
}
|
||||
val := fi.Value
|
||||
name := fi.Name
|
||||
name := fi.Selector
|
||||
param.Name = name
|
||||
if def, ok := val.Default(); ok && def.IsConcrete() {
|
||||
param.Default = velacue.GetDefault(def)
|
||||
@@ -212,19 +266,18 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
|
||||
case cue.StructKind:
|
||||
if subField, err := val.Struct(); err == nil && subField.Len() == 0 { // err cannot be not nil,so ignore it
|
||||
if mapValue, ok := val.Elem(); ok {
|
||||
var ident *ast.Ident
|
||||
if source, ok := mapValue.Source().(*ast.Ident); ok {
|
||||
ident = source
|
||||
}
|
||||
if source, ok := mapValue.Source().(*ast.Field); ok {
|
||||
if v, ok := source.Value.(*ast.Ident); ok {
|
||||
ident = v
|
||||
indentName := getIndentName(mapValue)
|
||||
_, err := mapValue.Fields()
|
||||
if err == nil {
|
||||
subDoc, subConsole, err := ref.parseParameters(capName, mapValue, indentName, depth+1, containSuffix)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
if ident != nil && len(ident.Name) != 0 {
|
||||
param.PrintableType = fmt.Sprintf("map[string]:%s", ident.Name)
|
||||
param.PrintableType = fmt.Sprintf("map[string]%s(#%s%s)", indentName, strings.ToLower(indentName), suffixRef)
|
||||
doc += subDoc
|
||||
console = append(console, subConsole...)
|
||||
} else {
|
||||
param.PrintableType = fmt.Sprintf("map[string]:%s", mapValue.IncompleteKind().String())
|
||||
param.PrintableType = "map[string]" + mapValue.IncompleteKind().String()
|
||||
}
|
||||
} else {
|
||||
param.PrintableType = val.IncompleteKind().String()
|
||||
@@ -234,7 +287,10 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
|
||||
if op == cue.OrOp {
|
||||
var printTypes []string
|
||||
for idx, ev := range elements {
|
||||
opName := fmt.Sprintf("%s-option-%d", name, idx)
|
||||
opName := getIndentName(ev)
|
||||
if opName == "struct" {
|
||||
opName = fmt.Sprintf("type-option-%d", idx+1)
|
||||
}
|
||||
subDoc, subConsole, err := ref.parseParameters(capName, ev, opName, depth+1, containSuffix)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
@@ -276,16 +332,35 @@ func (ref *ParseReference) parseParameters(capName string, paraValue cue.Value,
|
||||
param.PrintableType = fmt.Sprintf("[]%s", elem.IncompleteKind().String())
|
||||
}
|
||||
default:
|
||||
param.PrintableType = param.Type.String()
|
||||
param.PrintableType = getConcreteOrValueType(val)
|
||||
}
|
||||
params = append(params, param)
|
||||
}
|
||||
default:
|
||||
var param ReferenceParameter
|
||||
// TODO better composition type handling, see command trait.
|
||||
param.Name = "--"
|
||||
param.Usage = "Composition type"
|
||||
param.PrintableType = extractTypeFromError(paraValue)
|
||||
op, elements := paraValue.Expr()
|
||||
if op == cue.OrOp {
|
||||
var printTypes []string
|
||||
for idx, ev := range elements {
|
||||
opName := getIndentName(ev)
|
||||
if opName == "struct" {
|
||||
opName = fmt.Sprintf("type-option-%d", idx+1)
|
||||
}
|
||||
subDoc, subConsole, err := ref.parseParameters(capName, ev, opName, depth+1, containSuffix)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
printTypes = append(printTypes, fmt.Sprintf("[%s](#%s%s)", opName, strings.ToLower(opName), suffixRef))
|
||||
doc += subDoc
|
||||
console = append(console, subConsole...)
|
||||
}
|
||||
param.PrintableType = strings.Join(printTypes, " or ")
|
||||
} else {
|
||||
// TODO more composition type to be handle here
|
||||
param.Name = "--"
|
||||
param.Usage = "Unsupported Composition Type"
|
||||
param.PrintableType = extractTypeFromError(paraValue)
|
||||
}
|
||||
params = append(params, param)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ package docgen
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -562,34 +561,272 @@ func TestParseLocalFile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractParameter(t *testing.T) {
|
||||
ref := &ConsoleReference{}
|
||||
cueTemplate := `
|
||||
parameter: {
|
||||
testcases := map[string]struct {
|
||||
cueTemplate string
|
||||
contains string
|
||||
}{
|
||||
"normal-case": {
|
||||
cueTemplate: `parameter: {
|
||||
str: string
|
||||
itr: int
|
||||
btr: bool
|
||||
ct: cts: string
|
||||
}`,
|
||||
contains: `### normal-case
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
str | | string | true |
|
||||
itr | | int | true |
|
||||
btr | | bool | true |
|
||||
ct | | [ct](#ct) | true |
|
||||
|
||||
|
||||
#### ct
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
cts | | string | true |`,
|
||||
},
|
||||
"normal-map-string-string": {
|
||||
cueTemplate: `parameter: {
|
||||
envMappings: [string]: string
|
||||
}`,
|
||||
contains: `### normal-map-string-string
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
envMappings | | map[string]string | true |`,
|
||||
},
|
||||
"normal-map-case": {
|
||||
cueTemplate: `parameter: {
|
||||
// +usage=The mapping of environment variables to secret
|
||||
envMappings: [string]: #KeySecret
|
||||
}
|
||||
#KeySecret: {
|
||||
key?: string
|
||||
secret: string
|
||||
}
|
||||
`
|
||||
oldStdout := os.Stdout
|
||||
defer func() {
|
||||
os.Stdout = oldStdout
|
||||
}()
|
||||
}`,
|
||||
contains: `### normal-map-case
|
||||
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
cueValue, _ := common.GetCUEParameterValue(cueTemplate, nil)
|
||||
defaultDepth := 0
|
||||
defaultDisplay := "console"
|
||||
ref.DisplayFormat = defaultDisplay
|
||||
_, console, err := ref.parseParameters("", cueValue, Specification, defaultDepth, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(console))
|
||||
console[0].TableObject.Render()
|
||||
err = w.Close()
|
||||
assert.NoError(t, err)
|
||||
out, _ := ioutil.ReadAll(r)
|
||||
assert.Contains(t, string(out), "map[string]:#KeySecret")
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
envMappings | The mapping of environment variables to secret. | map[string]KeySecret(#keysecret) | true |
|
||||
|
||||
|
||||
#### KeySecret
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
key | | string | false |
|
||||
secret | | string | true |`,
|
||||
},
|
||||
"or-case-with-type": {
|
||||
cueTemplate: ` parameter: {
|
||||
orValue: #KeyConfig | #KeySecret
|
||||
}
|
||||
|
||||
#KeySecret: {
|
||||
key: "abc"
|
||||
secret: string
|
||||
}
|
||||
|
||||
#KeyConfig: {
|
||||
key: "abc"
|
||||
config: string
|
||||
}`,
|
||||
contains: `### or-case-with-type
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
orValue | | [KeyConfig](#keyconfig) or [KeySecret](#keysecret) | true |
|
||||
|
||||
|
||||
#### KeyConfig
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
key | | string | true |
|
||||
config | | string | true |
|
||||
|
||||
|
||||
#### KeySecret
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
key | | string | true |
|
||||
secret | | string | true | `,
|
||||
},
|
||||
"or-type-with-const-str": {
|
||||
cueTemplate: `parameter: {
|
||||
type: *"configMap" | "secret" | "emptyDir" | "ephemeral"
|
||||
}`,
|
||||
contains: `### or-type-with-const-str
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
type | | "configMap" or "secret" or "emptyDir" or "ephemeral" | false | configMap`,
|
||||
},
|
||||
"or-type-with-const-and-string": {
|
||||
cueTemplate: `parameter: {
|
||||
type: *"configMap" | "secret" | "emptyDir" | string
|
||||
}`,
|
||||
contains: `### or-type-with-const-and-string
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
type | | string | false | configMap`,
|
||||
},
|
||||
"var-or-with-struct-var": {
|
||||
cueTemplate: `
|
||||
parameter: {
|
||||
orValue: KeyConfig | KeySecret
|
||||
}
|
||||
|
||||
KeySecret: {
|
||||
key: "abc"
|
||||
secret: string
|
||||
}
|
||||
|
||||
KeyConfig: {
|
||||
key: "abc"
|
||||
config: string
|
||||
}`,
|
||||
contains: `### var-or-with-struct-var
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
orValue | | [KeyConfig](#keyconfig) or [KeySecret](#keysecret) | true |
|
||||
|
||||
|
||||
#### KeyConfig
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
key | | string | true |
|
||||
config | | string | true |
|
||||
|
||||
|
||||
#### KeySecret
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
key | | string | true |
|
||||
secret | | string | true | `,
|
||||
},
|
||||
}
|
||||
|
||||
ref := &MarkdownReference{}
|
||||
for key, ca := range testcases {
|
||||
cueValue, _ := common.GetCUEParameterValue(ca.cueTemplate, nil)
|
||||
out, _, err := ref.parseParameters("", cueValue, key, 0, false)
|
||||
assert.NoError(t, err, key)
|
||||
assert.Contains(t, out, ca.contains, key)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractParameterFromFiles(t *testing.T) {
|
||||
ref := &MarkdownReference{}
|
||||
testcases := map[string]struct {
|
||||
path string
|
||||
contains string
|
||||
}{
|
||||
"env": {
|
||||
path: "testdata/parameter/env.cue",
|
||||
contains: `### env
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
| | [PatchParams](#patchparams) or [type-option-2](#type-option-2) | false |
|
||||
|
||||
|
||||
#### PatchParams
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
containerName | Specify the name of the target container, if not set, use the component name. | string | false | empty
|
||||
replace | Specify if replacing the whole environment settings for the container. | bool | false | false
|
||||
env | Specify the environment variables to merge, if key already existing, override its value. | map[string]string | true |
|
||||
unset | Specify which existing environment variables to unset. | []string | true |
|
||||
|
||||
|
||||
#### type-option-2
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
containers | Specify the environment variables for multiple containers. | [[]containers](#containers) | true |
|
||||
|
||||
|
||||
##### containers
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
containerName | Specify the name of the target container, if not set, use the component name. | string | false | empty
|
||||
replace | Specify if replacing the whole environment settings for the container. | bool | false | false
|
||||
env | Specify the environment variables to merge, if key already existing, override its value. | map[string]string | true |
|
||||
unset | Specify which existing environment variables to unset. | []string | true |`,
|
||||
},
|
||||
"command": {
|
||||
path: "testdata/parameter/command.cue",
|
||||
contains: `### command
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
| | [PatchParams](#patchparams) or [type-option-2](#type-option-2) | false |
|
||||
|
||||
|
||||
#### PatchParams
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
containerName | Specify the name of the target container, if not set, use the component name. | string | false | empty
|
||||
command | Specify the command to use in the target container, if not set, it will not be changed. | null | true |
|
||||
args | Specify the args to use in the target container, if set, it will override existing args. | null | true |
|
||||
addArgs | Specify the args to add in the target container, existing args will be kept, cannot be used with args. | null | true |
|
||||
delArgs | Specify the existing args to delete in the target container, cannot be used with args. | null | true |
|
||||
|
||||
|
||||
#### type-option-2
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
containers | Specify the commands for multiple containers. | [[]containers](#containers) | true |
|
||||
|
||||
|
||||
##### containers
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
containerName | Specify the name of the target container, if not set, use the component name. | string | false | empty
|
||||
command | Specify the command to use in the target container, if not set, it will not be changed. | null | true |
|
||||
args | Specify the args to use in the target container, if set, it will override existing args. | null | true |
|
||||
addArgs | Specify the args to add in the target container, existing args will be kept, cannot be used with args. | null | true |
|
||||
delArgs | Specify the existing args to delete in the target container, cannot be used with args. | null | true |`,
|
||||
},
|
||||
"condition": {
|
||||
path: "testdata/parameter/condition.cue",
|
||||
contains: `### condition
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
volumes | | [[]volumes](#volumes) | true |
|
||||
|
||||
|
||||
#### volumes
|
||||
|
||||
Name | Description | Type | Required | Default
|
||||
---- | ----------- | ---- | -------- | -------
|
||||
name | | string | true |
|
||||
defaultMode | only works when type equals configmap. | int | false | 420
|
||||
type | | "configMap" or "secret" or "emptyDir" or "ephemeral" | false | configMap`,
|
||||
},
|
||||
}
|
||||
for key, ca := range testcases {
|
||||
content, err := os.ReadFile(ca.path)
|
||||
assert.NoError(t, err, ca.path)
|
||||
cueValue, _ := common.GetCUEParameterValue(string(content), nil)
|
||||
out, _, err := ref.parseParameters("", cueValue, key, 0, false)
|
||||
assert.NoError(t, err, key)
|
||||
assert.Contains(t, out, ca.contains, key)
|
||||
}
|
||||
}
|
||||
|
||||
17
references/docgen/testdata/parameter/command.cue
vendored
Normal file
17
references/docgen/testdata/parameter/command.cue
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
#PatchParams: {
|
||||
// +usage=Specify the name of the target container, if not set, use the component name
|
||||
containerName: *"" | string
|
||||
// +usage=Specify the command to use in the target container, if not set, it will not be changed
|
||||
command: *null | [...string]
|
||||
// +usage=Specify the args to use in the target container, if set, it will override existing args
|
||||
args: *null | [...string]
|
||||
// +usage=Specify the args to add in the target container, existing args will be kept, cannot be used with args
|
||||
addArgs: *null | [...string]
|
||||
// +usage=Specify the existing args to delete in the target container, cannot be used with args
|
||||
delArgs: *null | [...string]
|
||||
}
|
||||
|
||||
parameter: *#PatchParams | close({
|
||||
// +usage=Specify the commands for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
10
references/docgen/testdata/parameter/condition.cue
vendored
Normal file
10
references/docgen/testdata/parameter/condition.cue
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
parameter: {
|
||||
volumes: [...{
|
||||
name: string
|
||||
type: *"configMap" | "secret" | "emptyDir" | "ephemeral"
|
||||
if type == "configMap" {
|
||||
//+usage=only works when type equals configmap
|
||||
defaultMode: *420 | int
|
||||
}},
|
||||
]
|
||||
}
|
||||
14
references/docgen/testdata/parameter/env.cue
vendored
Normal file
14
references/docgen/testdata/parameter/env.cue
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#PatchParams: {
|
||||
// +usage=Specify the name of the target container, if not set, use the component name
|
||||
containerName: *"" | string
|
||||
// +usage=Specify if replacing the whole environment settings for the container
|
||||
replace: *false | bool
|
||||
// +usage=Specify the environment variables to merge, if key already existing, override its value
|
||||
env: [string]: string
|
||||
// +usage=Specify which existing environment variables to unset
|
||||
unset: *[] | [...string]
|
||||
}
|
||||
parameter: *#PatchParams | close({
|
||||
// +usage=Specify the environment variables for multiple containers
|
||||
containers: [...#PatchParams]
|
||||
})
|
||||
@@ -49,6 +49,8 @@ rules:
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- statefulsets
|
||||
- statefulsets/status
|
||||
- deployments
|
||||
- deployments/status
|
||||
- controllerrevisions
|
||||
|
||||
@@ -18,14 +18,16 @@ package e2e_multicluster_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gbytes"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -36,6 +38,7 @@ import (
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Test multicluster CLI commands", func() {
|
||||
@@ -48,7 +51,7 @@ var _ = Describe("Test multicluster CLI commands", func() {
|
||||
BeforeEach(func() {
|
||||
hubCtx, workerCtx, namespace = initializeContextAndNamespace()
|
||||
app = &v1beta1.Application{}
|
||||
bs, err := ioutil.ReadFile("./testdata/app/example-vela-cli-tool-test-app.yaml")
|
||||
bs, err := os.ReadFile("./testdata/app/example-vela-cli-tool-test-app.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
appYaml := strings.ReplaceAll(string(bs), "TEST_NAMESPACE", namespace)
|
||||
Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
|
||||
@@ -87,7 +90,7 @@ var _ = Describe("Test multicluster CLI commands", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
command := exec.Command("vela", "port-forward", app.Name, "-n", namespace)
|
||||
session, err := gexec.Start(command, ioutil.Discard, ioutil.Discard)
|
||||
session, err := gexec.Start(command, io.Discard, io.Discard)
|
||||
Expect(err).Should(Succeed())
|
||||
<-stopChannel
|
||||
session.Terminate()
|
||||
@@ -113,7 +116,7 @@ var _ = Describe("Test multicluster CLI commands", func() {
|
||||
for _, format := range []string{"inline", "wide", "table", "list"} {
|
||||
outputs, err := execCommand("status", app.Name, "-n", namespace, "--tree", "--detail", "--detail-format", format)
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(string(outputs)).Should(SatisfyAll(
|
||||
Expect(outputs).Should(SatisfyAll(
|
||||
ContainSubstring("alias-worker-tree"),
|
||||
ContainSubstring("Deployment/exec-podinfo"),
|
||||
ContainSubstring("updated"),
|
||||
@@ -121,6 +124,38 @@ var _ = Describe("Test multicluster CLI commands", func() {
|
||||
))
|
||||
}
|
||||
})
|
||||
|
||||
It("Test vela logs", func() {
|
||||
var (
|
||||
err error
|
||||
session *gexec.Session
|
||||
waitingTime = 2 * time.Minute
|
||||
)
|
||||
podViewCMFile, err := os.ReadFile("./testdata/view/component-pod-view.yaml")
|
||||
Expect(err).Should(BeNil())
|
||||
podViewCM := &v1.ConfigMap{}
|
||||
Expect(yaml.Unmarshal(podViewCMFile, podViewCM)).Should(BeNil())
|
||||
Expect(k8sClient.Create(hubCtx, podViewCM)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
stopChannel := make(chan struct{}, 1)
|
||||
defer func() {
|
||||
stopChannel <- struct{}{}
|
||||
}()
|
||||
|
||||
command := exec.Command("vela", "logs", app.Name, "-n", namespace, "--cluster", WorkerClusterName)
|
||||
session, err = gexec.Start(command, nil, nil)
|
||||
Expect(err).Should(Succeed())
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
<-stopChannel
|
||||
session.Terminate()
|
||||
Eventually(session, 10*time.Second).Should(gexec.Exit())
|
||||
}()
|
||||
Expect(err).Should(Succeed())
|
||||
Eventually(session, waitingTime).ShouldNot(BeNil())
|
||||
Eventually(session, waitingTime).Should(gbytes.Say("exec-podinfo"))
|
||||
Eventually(session, waitingTime).Should(gbytes.Say("httpd started"))
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
@@ -714,5 +714,87 @@ var _ = Describe("Test multicluster scenario", func() {
|
||||
Expect(cm.Data["cluster"]).Should(Equal("cluster-worker"))
|
||||
Expect(k8sClient.Delete(hubCtx, def)).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(10 * 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["endpoint"] = []byte("https://1.2.3.4:9999")
|
||||
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(10 * 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("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(30 * time.Second).WithPolling(2 * 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"]
|
||||
75
test/e2e-multicluster-test/testdata/view/component-pod-view.yaml
vendored
Normal file
75
test/e2e-multicluster-test/testdata/view/component-pod-view.yaml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: component-pod-view
|
||||
namespace: vela-system
|
||||
data:
|
||||
template: |
|
||||
import (
|
||||
"vela/ql"
|
||||
)
|
||||
|
||||
parameter: {
|
||||
appName: string
|
||||
appNs: string
|
||||
name?: string
|
||||
cluster?: string
|
||||
clusterNs?: string
|
||||
}
|
||||
|
||||
result: ql.#CollectPods & {
|
||||
app: {
|
||||
name: parameter.appName
|
||||
namespace: parameter.appNs
|
||||
filter: {
|
||||
if parameter.cluster != _|_ {
|
||||
cluster: parameter.cluster
|
||||
}
|
||||
if parameter.clusterNs != _|_ {
|
||||
clusterNamespace: parameter.clusterNs
|
||||
}
|
||||
if parameter.name != _|_ {
|
||||
components: [parameter.name]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.err == _|_ {
|
||||
status: {
|
||||
podList: [ for pod in result.list if pod.object != _|_ {
|
||||
cluster: pod.cluster
|
||||
workload: pod.workload
|
||||
component: pod.component
|
||||
metadata: {
|
||||
name: pod.object.metadata.name
|
||||
namespace: pod.object.metadata.namespace
|
||||
creationTime: pod.object.metadata.creationTimestamp
|
||||
labels: pod.object.metadata.labels
|
||||
version: {
|
||||
if pod.publishVersion != _|_ {
|
||||
publishVersion: pod.publishVersion
|
||||
}
|
||||
if pod.deployVersion != _|_ {
|
||||
deployVersion: pod.deployVersion
|
||||
}
|
||||
}
|
||||
}
|
||||
status: {
|
||||
phase: pod.object.status.phase
|
||||
// refer to https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
if phase != "Pending" && phase != "Unknown" {
|
||||
podIP: pod.object.status.podIP
|
||||
hostIP: pod.object.status.hostIP
|
||||
nodeName: pod.object.spec.nodeName
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
if result.err != _|_ {
|
||||
status: {
|
||||
error: result.err
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user