mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-28 08:43:57 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b11eb845b2 | ||
|
|
b12968c2ae | ||
|
|
d8d0c91c59 | ||
|
|
5f6209e8de | ||
|
|
a198fa5f9a | ||
|
|
f01639e175 | ||
|
|
b24b52b651 | ||
|
|
ae91006a3d | ||
|
|
42d6791476 | ||
|
|
c85502e54a | ||
|
|
f9a6b22294 | ||
|
|
5085a99a12 |
@@ -333,7 +333,8 @@ type WorkflowStatus struct {
|
||||
Steps []workflowv1alpha1.WorkflowStepStatus `json:"steps,omitempty"`
|
||||
|
||||
StartTime metav1.Time `json:"startTime,omitempty"`
|
||||
EndTime metav1.Time `json:"endTime,omitempty"`
|
||||
// +nullable
|
||||
EndTime metav1.Time `json:"endTime,omitempty"`
|
||||
}
|
||||
|
||||
// DefinitionType describes the type of DefinitionRevision.
|
||||
|
||||
@@ -844,6 +844,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -2759,6 +2760,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -4813,6 +4815,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
|
||||
@@ -768,6 +768,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -1530,6 +1531,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
|
||||
@@ -844,6 +844,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -2759,6 +2760,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -4813,6 +4815,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
|
||||
@@ -768,6 +768,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -1530,6 +1531,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
|
||||
2
go.mod
2
go.mod
@@ -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
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1337,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=
|
||||
|
||||
@@ -844,6 +844,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -2759,6 +2760,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -4813,6 +4815,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
|
||||
@@ -769,6 +769,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
@@ -1531,6 +1532,7 @@ spec:
|
||||
type: object
|
||||
endTime:
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
finished:
|
||||
type: boolean
|
||||
|
||||
@@ -17,9 +17,12 @@ limitations under the License.
|
||||
package addon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -29,6 +32,38 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func setupMockServer() *httptest.Server {
|
||||
var listenURL string
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
fileList := []string{
|
||||
"index.yaml",
|
||||
"fluxcd-test-version-1.0.0.tgz",
|
||||
"fluxcd-test-version-2.0.0.tgz",
|
||||
"vela-workflow-v0.3.5.tgz",
|
||||
"foo-v1.0.0.tgz",
|
||||
"bar-v1.0.0.tgz",
|
||||
"bar-v2.0.0.tgz",
|
||||
"mock-be-dep-addon-v1.0.0.tgz",
|
||||
}
|
||||
for _, f := range fileList {
|
||||
if strings.Contains(req.URL.Path, f) {
|
||||
file, err := os.ReadFile("../../e2e/addon/mock/testrepo/helm-repo/" + f)
|
||||
if err != nil {
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
}
|
||||
if f == "index.yaml" {
|
||||
// in index.yaml, url is hardcoded to 127.0.0.1:9098,
|
||||
// so we need to replace it with the real random listen url
|
||||
file = bytes.ReplaceAll(file, []byte("http://127.0.0.1:9098"), []byte(listenURL))
|
||||
}
|
||||
_, _ = w.Write(file)
|
||||
}
|
||||
}
|
||||
}))
|
||||
listenURL = s.URL
|
||||
return s
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -50,12 +85,15 @@ var _ = Describe("test FindAddonPackagesDetailFromRegistry", func() {
|
||||
})
|
||||
|
||||
Describe("one versioned registry is added", func() {
|
||||
var s *httptest.Server
|
||||
|
||||
BeforeEach(func() {
|
||||
// Prepare KubeVela registry
|
||||
s = setupMockServer()
|
||||
// Prepare registry
|
||||
reg := &Registry{
|
||||
Name: "KubeVela",
|
||||
Name: "addon_helper_test",
|
||||
Helm: &HelmSource{
|
||||
URL: "https://addons.kubevela.net",
|
||||
URL: s.URL,
|
||||
},
|
||||
}
|
||||
ds := NewRegistryDataStore(k8sClient)
|
||||
@@ -63,38 +101,36 @@ var _ = Describe("test FindAddonPackagesDetailFromRegistry", func() {
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// Clean up KubeVela registry
|
||||
// Clean up registry
|
||||
ds := NewRegistryDataStore(k8sClient)
|
||||
Expect(ds.DeleteRegistry(context.Background(), "KubeVela")).To(Succeed())
|
||||
Expect(ds.DeleteRegistry(context.Background(), "addon_helper_test")).To(Succeed())
|
||||
s.Close()
|
||||
})
|
||||
|
||||
Context("when empty addonNames and registryNames is supplied", func() {
|
||||
It("should return error, empty addonNames are not allowed", func() {
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{"KubeVela"})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{}, []string{"addon_helper_test"})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("should return error, empty addonNames are not allowed", func() {
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, []string{"KubeVela"})
|
||||
_, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, nil, []string{"addon_helper_test"})
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("one existing addon name provided", func() {
|
||||
It("should return one valid result, matching all registries", func() {
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"foo"}, nil)
|
||||
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
Expect(res[0].Name).To(Equal("foo"))
|
||||
})
|
||||
It("should return one valid result, matching one registry", func() {
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux"}, []string{"KubeVela"})
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"foo"}, []string{"addon_helper_test"})
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
Expect(res[0].Name).To(Equal("foo"))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -108,26 +144,20 @@ var _ = Describe("test FindAddonPackagesDetailFromRegistry", func() {
|
||||
|
||||
Context("two existing addon names provided", func() {
|
||||
It("should return two valid result", func() {
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux", "traefik"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"foo", "bar"}, nil)
|
||||
Expect(err).To(Succeed())
|
||||
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())
|
||||
Expect(res[0].Name).To(Equal("foo"))
|
||||
Expect(res[1].Name).To(Equal("bar"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("one existing addon name and one non-existent addon name provided", func() {
|
||||
It("should return only one valid result", func() {
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"velaux", "non-existent-addon"}, nil)
|
||||
res, err := FindAddonPackagesDetailFromRegistry(context.Background(), k8sClient, []string{"foo", "non-existent-addon"}, nil)
|
||||
Expect(err).To(Succeed())
|
||||
Expect(res).To(HaveLen(1))
|
||||
Expect(res[0].Name).To(Equal("velaux"))
|
||||
Expect(res[0].InstallPackage).ToNot(BeNil())
|
||||
Expect(res[0].APISchema).ToNot(BeNil())
|
||||
Expect(res[0].Name).To(Equal("foo"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -50,7 +50,7 @@ type Workflow struct {
|
||||
|
||||
// WorkflowStep defines how to execute a workflow step.
|
||||
type WorkflowStep struct {
|
||||
WorkflowStepBase `json:",inline"`
|
||||
WorkflowStepBase `json:",inline" bson:",inline"`
|
||||
SubSteps []WorkflowStepBase `json:"subSteps,omitempty"`
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ type WorkflowRecord struct {
|
||||
|
||||
// WorkflowStepStatus is the workflow step status database model
|
||||
type WorkflowStepStatus struct {
|
||||
StepStatus `json:",inline"`
|
||||
StepStatus `json:",inline" bson:",inline"`
|
||||
SubStepsStatus []StepStatus `json:"subSteps,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -137,6 +137,14 @@ func (u *configServiceImpl) CreateConfig(ctx context.Context, project string, re
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
exist, err := u.Factory.IsExist(ctx, ns, req.Name)
|
||||
if err != nil {
|
||||
klog.Errorf("check config name is exist failure %s", err.Error())
|
||||
return nil, bcode.ErrConfigExist
|
||||
}
|
||||
if exist {
|
||||
return nil, bcode.ErrConfigExist
|
||||
}
|
||||
var properties = make(map[string]interface{})
|
||||
if err := json.Unmarshal([]byte(req.Properties), &properties); err != nil {
|
||||
return nil, err
|
||||
@@ -156,9 +164,6 @@ func (u *configServiceImpl) CreateConfig(ctx context.Context, project string, re
|
||||
return nil, err
|
||||
}
|
||||
if err := u.Factory.CreateOrUpdateConfig(ctx, configItem, ns); err != nil {
|
||||
if errors.Is(err, config.ErrConfigExist) {
|
||||
return nil, bcode.ErrConfigExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return convertConfig(project, *configItem), nil
|
||||
@@ -196,9 +201,12 @@ func (u *configServiceImpl) UpdateConfig(ctx context.Context, project string, na
|
||||
return nil, err
|
||||
}
|
||||
if err := u.Factory.CreateOrUpdateConfig(ctx, configItem, ns); err != nil {
|
||||
if errors.Is(err, config.ErrConfigExist) {
|
||||
if errors.Is(err, config.ErrChangeTemplate) {
|
||||
return nil, bcode.ErrChangeTemplate
|
||||
}
|
||||
if errors.Is(err, config.ErrChangeSecretType) {
|
||||
return nil, bcode.ErrChangeSecretType
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return convertConfig(project, *configItem), nil
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -37,4 +37,7 @@ var (
|
||||
|
||||
// ErrNotFoundDistribution means the distribution is not exist
|
||||
ErrNotFoundDistribution = NewBcode(404, 16007, "the distribution is not exist")
|
||||
|
||||
// ErrChangeSecretType the secret type of the config can not be change
|
||||
ErrChangeSecretType = NewBcode(400, 16008, "the secret type of the config can not be change")
|
||||
)
|
||||
|
||||
@@ -88,6 +88,12 @@ var ErrConfigNotFound = errors.New("the config is not exist")
|
||||
// ErrTemplateNotFound means the template is not exist
|
||||
var ErrTemplateNotFound = errors.New("the template is not exist")
|
||||
|
||||
// ErrChangeTemplate means the template of the config can not be change
|
||||
var ErrChangeTemplate = errors.New("the template of the config can not be change")
|
||||
|
||||
// ErrChangeSecretType means the secret type of the config can not be change
|
||||
var ErrChangeSecretType = errors.New("the secret type of the config can not be change")
|
||||
|
||||
// NamespacedName the namespace and name model
|
||||
type NamespacedName struct {
|
||||
Name string `json:"name"`
|
||||
@@ -192,6 +198,7 @@ type Factory interface {
|
||||
ListConfigs(ctx context.Context, namespace, template, scope string, withStatus bool) ([]*Config, error)
|
||||
DeleteConfig(ctx context.Context, namespace, name string) error
|
||||
CreateOrUpdateConfig(ctx context.Context, i *Config, ns string) error
|
||||
IsExist(ctx context.Context, namespace, name string) (bool, error)
|
||||
|
||||
CreateOrUpdateDistribution(ctx context.Context, ns, name string, ads *CreateDistributionSpec) error
|
||||
ListDistributions(ctx context.Context, ns string) ([]*Distribution, error)
|
||||
@@ -549,7 +556,10 @@ func (k *kubeConfigFactory) CreateOrUpdateConfig(ctx context.Context, i *Config,
|
||||
var secret v1.Secret
|
||||
if err := k.cli.Get(ctx, pkgtypes.NamespacedName{Namespace: i.Namespace, Name: i.Name}, &secret); err == nil {
|
||||
if secret.Labels[types.LabelConfigType] != i.Template.Name {
|
||||
return ErrConfigExist
|
||||
return ErrChangeTemplate
|
||||
}
|
||||
if secret.Type != i.Secret.Type {
|
||||
return ErrChangeSecretType
|
||||
}
|
||||
}
|
||||
if err := k.apiApply.Apply(ctx, i.Secret, apply.Quiet()); err != nil {
|
||||
@@ -571,6 +581,17 @@ func (k *kubeConfigFactory) CreateOrUpdateConfig(ctx context.Context, i *Config,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *kubeConfigFactory) IsExist(ctx context.Context, namespace, name string) (bool, error) {
|
||||
var secret v1.Secret
|
||||
if err := k.cli.Get(ctx, pkgtypes.NamespacedName{Namespace: namespace, Name: name}, &secret); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (k *kubeConfigFactory) ListConfigs(ctx context.Context, namespace, template, scope string, withStatus bool) ([]*Config, error) {
|
||||
var list = &v1.SecretList{}
|
||||
requirement := fmt.Sprintf("%s=%s", types.LabelConfigCatalog, types.VelaCoreConfig)
|
||||
|
||||
@@ -145,6 +145,12 @@ var _ = Describe("test config factory", func() {
|
||||
Expect(len(config.Targets)).Should(Equal(1))
|
||||
})
|
||||
|
||||
It("check if the config exist", func() {
|
||||
exist, err := fac.IsExist(context.TODO(), "default", "db-config")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(exist).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("list the distributions", func() {
|
||||
distributions, err := fac.ListDistributions(context.TODO(), "default")
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v12 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
@@ -908,7 +909,7 @@ func iterateListSubResources(ctx context.Context, cluster string, k8sClient clie
|
||||
clusterCTX := multicluster.ContextWithClusterName(ctx, cluster)
|
||||
items, err := listItemByRule(clusterCTX, k8sClient, resource, *parentObject, specifiedFunc, rule.DefaultGenListOptionFunc, rule.DisableFilterByOwnerReference)
|
||||
if err != nil {
|
||||
if meta.IsNoMatchError(err) || runtime.IsNotRegisteredError(err) {
|
||||
if meta.IsNoMatchError(err) || runtime.IsNotRegisteredError(err) || kerrors.IsNotFound(err) {
|
||||
klog.Warningf("ignore list resources: %s as %v", resource.Kind, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -80,7 +80,7 @@ func NewUnInstallCommand(c common.Args, order string, ioStreams util.IOStreams)
|
||||
return errors.Wrapf(err, "cannot check installed addon")
|
||||
}
|
||||
if len(addons) != 0 {
|
||||
return fmt.Errorf("these addons have been eanbled :%v, please guarantee there is no application using these addons and use `vela uninstall -f` uninstall include addon ", addons)
|
||||
return fmt.Errorf("these addons have been enabled :%v, please guarantee there is no application using these addons and use `vela uninstall -f` uninstall include addon ", addons)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ rules:
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- statefulsets
|
||||
- statefulsets/status
|
||||
- deployments
|
||||
- deployments/status
|
||||
- controllerrevisions
|
||||
|
||||
@@ -153,7 +153,7 @@ var _ = Describe("Test the rest api about the config", func() {
|
||||
Expect(config.Secret).Should(BeNil())
|
||||
Expect(config.Properties["registry"]).Should(Equal("kubevela.test.com"))
|
||||
|
||||
By("the template is not exist")
|
||||
By("the config name is exist")
|
||||
req = v1.CreateConfigRequest{
|
||||
Name: "test-registry",
|
||||
Alias: "Test Registry",
|
||||
@@ -162,6 +162,17 @@ var _ = Describe("Test the rest api about the config", func() {
|
||||
Properties: `{"registry": "kubevela.test.com"}`,
|
||||
}
|
||||
res = post("/configs", req)
|
||||
Expect(res.StatusCode).Should(Equal(400))
|
||||
|
||||
By("the template is not exist")
|
||||
req = v1.CreateConfigRequest{
|
||||
Name: "test-registry2",
|
||||
Alias: "Test Registry",
|
||||
Description: "This is a demo config",
|
||||
Template: v1.NamespacedName{Name: templateName + "notfound"},
|
||||
Properties: `{"registry": "kubevela.test.com"}`,
|
||||
}
|
||||
res = post("/configs", req)
|
||||
Expect(res.StatusCode).Should(Equal(404))
|
||||
|
||||
By("without the template")
|
||||
|
||||
@@ -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"]
|
||||
Reference in New Issue
Block a user