mirror of https://github.com/kubevela/kubevela.git
Chore: remove manualscaler (#4716)
* chore: remove manualscaler CRD define in apis/ Signed-off-by: arcosx <arcosx@outlook.com> * chore: remove manualscaler CRDs yaml in charts/ Signed-off-by: arcosx <arcosx@outlook.com> * chore: remove manual scaler design in design/ Signed-off-by: arcosx <arcosx@outlook.com> * chore: remove manual scaler code in doc/ hack/ legacy/ references/ Signed-off-by: arcosx <arcosx@outlook.com> * chore: remove manual scaler code in pkg/ test/ vela-templates/ Signed-off-by: arcosx <arcosx@outlook.com> * chore: fix some code Signed-off-by: arcosx <arcosx@outlook.com> * Chore: remove manualscaler in test Signed-off-by: Somefive <yd219913@alibaba-inc.com> * Chore: remove outdated tests Signed-off-by: Somefive <yd219913@alibaba-inc.com> * Chore: remove outdated tests Signed-off-by: Somefive <yd219913@alibaba-inc.com> Signed-off-by: arcosx <arcosx@outlook.com> Signed-off-by: Somefive <yd219913@alibaba-inc.com> Co-authored-by: arcosx <arcosx@outlook.com>
This commit is contained in:
parent
1adc6d8803
commit
ac52f4aba8
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/condition"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
var _ oam.Trait = &ManualScalerTrait{}
|
||||
|
||||
// A ManualScalerTraitSpec defines the desired state of a ManualScalerTrait.
|
||||
type ManualScalerTraitSpec struct {
|
||||
// ReplicaCount of the workload this trait applies to.
|
||||
ReplicaCount int32 `json:"replicaCount"`
|
||||
|
||||
// WorkloadReference to the workload this trait applies to.
|
||||
WorkloadReference corev1.ObjectReference `json:"workloadRef"`
|
||||
}
|
||||
|
||||
// A ManualScalerTraitStatus represents the observed state of a
|
||||
// ManualScalerTrait.
|
||||
type ManualScalerTraitStatus struct {
|
||||
condition.ConditionedStatus `json:",inline"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// A ManualScalerTrait determines how many replicas a workload should have.
|
||||
// +kubebuilder:resource:categories={oam}
|
||||
// +kubebuilder:subresource:status
|
||||
type ManualScalerTrait struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ManualScalerTraitSpec `json:"spec,omitempty"`
|
||||
Status ManualScalerTraitStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// ManualScalerTraitList contains a list of ManualScalerTrait.
|
||||
type ManualScalerTraitList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []ManualScalerTrait `json:"items"`
|
||||
}
|
||||
|
|
@ -24,26 +24,6 @@ import (
|
|||
"github.com/oam-dev/kubevela/apis/core.oam.dev/condition"
|
||||
)
|
||||
|
||||
// GetCondition of this ManualScalerTrait.
|
||||
func (tr *ManualScalerTrait) GetCondition(ct condition.ConditionType) condition.Condition {
|
||||
return tr.Status.GetCondition(ct)
|
||||
}
|
||||
|
||||
// SetConditions of this ManualScalerTrait.
|
||||
func (tr *ManualScalerTrait) SetConditions(c ...condition.Condition) {
|
||||
tr.Status.SetConditions(c...)
|
||||
}
|
||||
|
||||
// GetWorkloadReference of this ManualScalerTrait.
|
||||
func (tr *ManualScalerTrait) GetWorkloadReference() corev1.ObjectReference {
|
||||
return tr.Spec.WorkloadReference
|
||||
}
|
||||
|
||||
// SetWorkloadReference of this ManualScalerTrait.
|
||||
func (tr *ManualScalerTrait) SetWorkloadReference(r corev1.ObjectReference) {
|
||||
tr.Spec.WorkloadReference = r
|
||||
}
|
||||
|
||||
// GetCondition of this ApplicationConfiguration.
|
||||
func (ac *ApplicationConfiguration) GetCondition(ct condition.ConditionType) condition.Condition {
|
||||
return ac.Status.GetCondition(ct)
|
||||
|
|
|
|||
|
|
@ -87,14 +87,6 @@ var (
|
|||
ApplicationConfigurationGroupVersionKind = SchemeGroupVersion.WithKind(ApplicationConfigurationKind)
|
||||
)
|
||||
|
||||
// ManualScalerTrait type metadata.
|
||||
var (
|
||||
ManualScalerTraitKind = reflect.TypeOf(ManualScalerTrait{}).Name()
|
||||
ManualScalerTraitGroupKind = schema.GroupKind{Group: Group, Kind: ManualScalerTraitKind}.String()
|
||||
ManualScalerTraitKindAPIVersion = ManualScalerTraitKind + "." + SchemeGroupVersion.String()
|
||||
ManualScalerTraitGroupVersionKind = SchemeGroupVersion.WithKind(ManualScalerTraitKind)
|
||||
)
|
||||
|
||||
// HealthScope type metadata.
|
||||
var (
|
||||
HealthScopeKind = reflect.TypeOf(HealthScope{}).Name()
|
||||
|
|
@ -126,7 +118,6 @@ func init() {
|
|||
SchemeBuilder.Register(&ScopeDefinition{}, &ScopeDefinitionList{})
|
||||
SchemeBuilder.Register(&Component{}, &ComponentList{})
|
||||
SchemeBuilder.Register(&ApplicationConfiguration{}, &ApplicationConfigurationList{})
|
||||
SchemeBuilder.Register(&ManualScalerTrait{}, &ManualScalerTraitList{})
|
||||
SchemeBuilder.Register(&HealthScope{}, &HealthScopeList{})
|
||||
SchemeBuilder.Register(&Application{}, &ApplicationList{})
|
||||
SchemeBuilder.Register(&ApplicationRevision{}, &ApplicationRevisionList{})
|
||||
|
|
|
|||
|
|
@ -1520,97 +1520,6 @@ func (in *HistoryWorkload) DeepCopy() *HistoryWorkload {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ManualScalerTrait) DeepCopyInto(out *ManualScalerTrait) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManualScalerTrait.
|
||||
func (in *ManualScalerTrait) DeepCopy() *ManualScalerTrait {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ManualScalerTrait)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ManualScalerTrait) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ManualScalerTraitList) DeepCopyInto(out *ManualScalerTraitList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]ManualScalerTrait, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManualScalerTraitList.
|
||||
func (in *ManualScalerTraitList) DeepCopy() *ManualScalerTraitList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ManualScalerTraitList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *ManualScalerTraitList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ManualScalerTraitSpec) DeepCopyInto(out *ManualScalerTraitSpec) {
|
||||
*out = *in
|
||||
out.WorkloadReference = in.WorkloadReference
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManualScalerTraitSpec.
|
||||
func (in *ManualScalerTraitSpec) DeepCopy() *ManualScalerTraitSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ManualScalerTraitSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ManualScalerTraitStatus) DeepCopyInto(out *ManualScalerTraitStatus) {
|
||||
*out = *in
|
||||
in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManualScalerTraitStatus.
|
||||
func (in *ManualScalerTraitStatus) DeepCopy() *ManualScalerTraitStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ManualScalerTraitStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MemoryResources) DeepCopyInto(out *MemoryResources) {
|
||||
*out = *in
|
||||
|
|
|
|||
|
|
@ -1,134 +0,0 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.6.2
|
||||
name: manualscalertraits.core.oam.dev
|
||||
spec:
|
||||
group: core.oam.dev
|
||||
names:
|
||||
categories:
|
||||
- oam
|
||||
kind: ManualScalerTrait
|
||||
listKind: ManualScalerTraitList
|
||||
plural: manualscalertraits
|
||||
singular: manualscalertrait
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: A ManualScalerTrait determines how many replicas a workload should
|
||||
have.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: A ManualScalerTraitSpec defines the desired state of a ManualScalerTrait.
|
||||
properties:
|
||||
replicaCount:
|
||||
description: ReplicaCount of the workload this trait applies to.
|
||||
format: int32
|
||||
type: integer
|
||||
workloadRef:
|
||||
description: WorkloadReference to the workload this trait applies
|
||||
to.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API version of the referent.
|
||||
type: string
|
||||
fieldPath:
|
||||
description: 'If referring to a piece of an object instead of
|
||||
an entire object, this string should contain a valid JSON/Go
|
||||
field access statement, such as desiredState.manifest.containers[2].
|
||||
For example, if the object reference is to a container within
|
||||
a pod, this would take on a value like: "spec.containers{name}"
|
||||
(where "name" refers to the name of the container that triggered
|
||||
the event) or if no container name is specified "spec.containers[2]"
|
||||
(container with index 2 in this pod). This syntax is chosen
|
||||
only to have some well-defined way of referencing a part of
|
||||
an object. TODO: this design is not final and this field is
|
||||
subject to change in the future.'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
|
||||
type: string
|
||||
resourceVersion:
|
||||
description: 'Specific resourceVersion to which this reference
|
||||
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
|
||||
type: string
|
||||
uid:
|
||||
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- replicaCount
|
||||
- workloadRef
|
||||
type: object
|
||||
status:
|
||||
description: A ManualScalerTraitStatus represents the observed state of
|
||||
a ManualScalerTrait.
|
||||
properties:
|
||||
conditions:
|
||||
description: Conditions of the resource.
|
||||
items:
|
||||
description: A Condition that may apply to a resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: LastTransitionTime is the last time this condition
|
||||
transitioned from one status to another.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: A Message containing details about this condition's
|
||||
last transition from one status to another, if any.
|
||||
type: string
|
||||
reason:
|
||||
description: A Reason for this condition's last transition from
|
||||
one status to another.
|
||||
type: string
|
||||
status:
|
||||
description: Status of this condition; is it currently True,
|
||||
False, or Unknown?
|
||||
type: string
|
||||
type:
|
||||
description: Type of this condition. At most one of each condition
|
||||
type may apply to a resource at any point in time.
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
|
@ -56,18 +56,18 @@ helm install --create-namespace -n vela-system kubevela kubevela/vela-minimal --
|
|||
|
||||
### KubeVela core parameters
|
||||
|
||||
| Name | Description | Value |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------- |
|
||||
| `systemDefinitionNamespace` | System definition namespace, if unspecified, will use built-in variable `.Release.Namespace`. | `nil` |
|
||||
| `applicationRevisionLimit` | Application revision limit | `10` |
|
||||
| `definitionRevisionLimit` | Definition revision limit | `20` |
|
||||
| `concurrentReconciles` | concurrentReconciles is the concurrent reconcile number of the controller | `4` |
|
||||
| `controllerArgs.reSyncPeriod` | The period for resync the applications | `5m` |
|
||||
| `OAMSpecVer` | OAMSpecVer is the oam spec version controller want to setup | `minimal` |
|
||||
| `disableCaps` | Disable capability | `manualscalertrait,envbinding,rollout` |
|
||||
| `applyOnceOnly` | Valid applyOnceOnly values: true/false/on/off/force | `off` |
|
||||
| `enableFluxcdAddon` | Whether to enable fluxcd addon | `false` |
|
||||
| `dependCheckWait` | dependCheckWait is the time to wait for ApplicationConfiguration's dependent-resource ready | `30s` |
|
||||
| Name | Description | Value |
|
||||
| ----------------------------- | --------------------------------------------------------------------------------------------- | -------------------- |
|
||||
| `systemDefinitionNamespace` | System definition namespace, if unspecified, will use built-in variable `.Release.Namespace`. | `nil` |
|
||||
| `applicationRevisionLimit` | Application revision limit | `10` |
|
||||
| `definitionRevisionLimit` | Definition revision limit | `20` |
|
||||
| `concurrentReconciles` | concurrentReconciles is the concurrent reconcile number of the controller | `4` |
|
||||
| `controllerArgs.reSyncPeriod` | The period for resync the applications | `5m` |
|
||||
| `OAMSpecVer` | OAMSpecVer is the oam spec version controller want to setup | `minimal` |
|
||||
| `disableCaps` | Disable capability | `envbinding,rollout` |
|
||||
| `applyOnceOnly` | Valid applyOnceOnly values: true/false/on/off/force | `off` |
|
||||
| `enableFluxcdAddon` | Whether to enable fluxcd addon | `false` |
|
||||
| `dependCheckWait` | dependCheckWait is the time to wait for ApplicationConfiguration's dependent-resource ready | `30s` |
|
||||
|
||||
|
||||
### KubeVela workflow parameters
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ controllerArgs:
|
|||
OAMSpecVer: "minimal"
|
||||
|
||||
## @param disableCaps Disable capability
|
||||
disableCaps: "manualscalertrait,envbinding,rollout"
|
||||
disableCaps: "envbinding,rollout"
|
||||
|
||||
## @param applyOnceOnly Valid applyOnceOnly values: true/false/on/off/force
|
||||
applyOnceOnly: "off"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Owner: Jianbo Sun (@wonderflow), Zhou Zheng Xi (@zzxwill)
|
||||
* Reviewers: KubeVela/Crossplane Maintainers
|
||||
* Status: Draft
|
||||
|
||||
* Notice: Manual scaler is deprecated. See this [issue](https://github.com/oam-dev/kubevela/issues/2262) to get more information.
|
||||
## Background
|
||||
|
||||
Now definition name is no longer coupled with CRD name, it's align to capability name in KubeVela,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ Server-side KubeVela(We call it `vela-core` for convenience) now includes follow
|
|||
| Control Plane Object | `components.core.oam.dev` | Yes | OAM Runtime |
|
||||
| Workload Type | `containerizedworklaods.core.oam.dev` | Yes | OAM Runtime |
|
||||
| Scope | `healthscope.core.oam.dev` | Yes | OAM Runtime |
|
||||
| Trait | `manualscalertraits.core.oam.dev` | Yes | OAM Runtime |
|
||||
| Control Plane Object | `scopedefinitions.core.oam.dev` | No | OAM Runtime |
|
||||
| Control Plane Object | `traitdefinitions.core.oam.dev` | No | OAM Runtime |
|
||||
| Control Plane Object | `workloaddefinitions.core.oam.dev` | No | OAM Runtime |
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Configures replicas for your service."
|
||||
name: myscaler
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
//+usage=Replicas of the workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ package e2e
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"github.com/oam-dev/kubevela/e2e"
|
||||
"github.com/oam-dev/kubevela/references/apis"
|
||||
|
|
@ -42,8 +41,6 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
var testTrait = "crd-manual-scaler"
|
||||
|
||||
// TODO: change this into a mock UT to avoid remote call.
|
||||
|
||||
var _ = Describe("test registry and trait/comp command", func() {
|
||||
|
|
@ -102,23 +99,6 @@ var _ = Describe("test registry and trait/comp command", func() {
|
|||
Expect(output).To(ContainSubstring("[deployments.apps]"))
|
||||
})
|
||||
|
||||
It("install traits to cluster", func() {
|
||||
cli := fmt.Sprintf("vela trait get %s", testTrait)
|
||||
output, err := e2e.Exec(cli)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
expectedSubStr1 := fmt.Sprintf("Installing trait %s", testTrait)
|
||||
expectedSubStr2 := fmt.Sprintf("Successfully install trait: %s", testTrait)
|
||||
Expect(output).To(ContainSubstring(expectedSubStr1))
|
||||
Expect(output).To(ContainSubstring(expectedSubStr2))
|
||||
})
|
||||
|
||||
It("Clean the test trait", func() {
|
||||
cmd := exec.Command("kubectl", "delete", "traitDefinition", testTrait, "-n", "vela-system")
|
||||
output, err := cmd.Output()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).Should(ContainSubstring("traitdefinition.core.oam.dev \"" + testTrait + "\" deleted"))
|
||||
})
|
||||
|
||||
It("test list trait in raw url", func() {
|
||||
cli := "vela trait --discover --url=oss://registry.kubevela.net"
|
||||
output, err := e2e.Exec(cli)
|
||||
|
|
|
|||
4
go.mod
4
go.mod
|
|
@ -53,7 +53,7 @@ require (
|
|||
github.com/imdario/mergo v0.3.12
|
||||
github.com/klauspost/compress v1.15.9
|
||||
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
|
||||
github.com/kubevela/pkg v0.0.0-20220907071953-81b2807e4bd1
|
||||
github.com/kubevela/pkg v0.0.0-20220913093640-a41193098f92
|
||||
github.com/kubevela/prism v1.4.1-0.20220613123457-94f1190f87c2
|
||||
github.com/kubevela/workflow v0.0.0-20220905111757-ae9387b554de
|
||||
github.com/kyokomi/emoji v2.2.4+incompatible
|
||||
|
|
@ -102,7 +102,6 @@ require (
|
|||
k8s.io/klog v1.0.0
|
||||
k8s.io/klog/v2 v2.60.1
|
||||
k8s.io/kube-aggregator v0.23.0
|
||||
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65
|
||||
k8s.io/kubectl v0.23.6
|
||||
k8s.io/metrics v0.23.6
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
|
||||
|
|
@ -308,6 +307,7 @@ require (
|
|||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
istio.io/api v0.0.0-20220512212136-561ffec82582 // indirect
|
||||
istio.io/gogo-genproto v0.0.0-20211208193508-5ab4acc9eb1e // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect
|
||||
oras.land/oras-go v0.4.0 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy v0.0.30 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30 // indirect
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -1379,8 +1379,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kubevela/pkg v0.0.0-20220905083925-331e331a12e1/go.mod h1:281yP5rMV+jYzxngUtM/FeZvAbqWEIow9tSamHHcDvI=
|
||||
github.com/kubevela/pkg v0.0.0-20220907071953-81b2807e4bd1 h1:kYz+x/AKtdXzWuciMSxavF7Y6dxcdaJ+OwMqe/7vvvY=
|
||||
github.com/kubevela/pkg v0.0.0-20220907071953-81b2807e4bd1/go.mod h1:281yP5rMV+jYzxngUtM/FeZvAbqWEIow9tSamHHcDvI=
|
||||
github.com/kubevela/pkg v0.0.0-20220913093640-a41193098f92 h1:B4GkHf6rdLC4x2Clya1GKAGGo683fHgNDE4IHAOSSKQ=
|
||||
github.com/kubevela/pkg v0.0.0-20220913093640-a41193098f92/go.mod h1:mGQEK3fAX9MkOGexLOm7H+h99SdqZSsUzTmVLgzpSdg=
|
||||
github.com/kubevela/prism v1.4.1-0.20220613123457-94f1190f87c2 h1:TaHlO4raKI3ehVSYY8QixYMHdI0VwKHY1KPNWcUre3I=
|
||||
github.com/kubevela/prism v1.4.1-0.20220613123457-94f1190f87c2/go.mod h1:RP69+bRb57Occer6BeeF5zK3hrD1IhnYf2RNRsIdh9E=
|
||||
github.com/kubevela/workflow v0.0.0-20220905111757-ae9387b554de h1:n5vkROgxW64Jq+Or6Ku9w/PDPRNypav2Ud1vJDxukWs=
|
||||
|
|
|
|||
|
|
@ -1,134 +0,0 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.6.2
|
||||
name: manualscalertraits.core.oam.dev
|
||||
spec:
|
||||
group: core.oam.dev
|
||||
names:
|
||||
categories:
|
||||
- oam
|
||||
kind: ManualScalerTrait
|
||||
listKind: ManualScalerTraitList
|
||||
plural: manualscalertraits
|
||||
singular: manualscalertrait
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha2
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: A ManualScalerTrait determines how many replicas a workload should
|
||||
have.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: A ManualScalerTraitSpec defines the desired state of a ManualScalerTrait.
|
||||
properties:
|
||||
replicaCount:
|
||||
description: ReplicaCount of the workload this trait applies to.
|
||||
format: int32
|
||||
type: integer
|
||||
workloadRef:
|
||||
description: WorkloadReference to the workload this trait applies
|
||||
to.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API version of the referent.
|
||||
type: string
|
||||
fieldPath:
|
||||
description: 'If referring to a piece of an object instead of
|
||||
an entire object, this string should contain a valid JSON/Go
|
||||
field access statement, such as desiredState.manifest.containers[2].
|
||||
For example, if the object reference is to a container within
|
||||
a pod, this would take on a value like: "spec.containers{name}"
|
||||
(where "name" refers to the name of the container that triggered
|
||||
the event) or if no container name is specified "spec.containers[2]"
|
||||
(container with index 2 in this pod). This syntax is chosen
|
||||
only to have some well-defined way of referencing a part of
|
||||
an object. TODO: this design is not final and this field is
|
||||
subject to change in the future.'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
name:
|
||||
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names'
|
||||
type: string
|
||||
namespace:
|
||||
description: 'Namespace of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/'
|
||||
type: string
|
||||
resourceVersion:
|
||||
description: 'Specific resourceVersion to which this reference
|
||||
is made, if any. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency'
|
||||
type: string
|
||||
uid:
|
||||
description: 'UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids'
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- replicaCount
|
||||
- workloadRef
|
||||
type: object
|
||||
status:
|
||||
description: A ManualScalerTraitStatus represents the observed state of
|
||||
a ManualScalerTrait.
|
||||
properties:
|
||||
conditions:
|
||||
description: Conditions of the resource.
|
||||
items:
|
||||
description: A Condition that may apply to a resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: LastTransitionTime is the last time this condition
|
||||
transitioned from one status to another.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: A Message containing details about this condition's
|
||||
last transition from one status to another, if any.
|
||||
type: string
|
||||
reason:
|
||||
description: A Reason for this condition's last transition from
|
||||
one status to another.
|
||||
type: string
|
||||
status:
|
||||
description: Status of this condition; is it currently True,
|
||||
False, or Unknown?
|
||||
type: string
|
||||
type:
|
||||
description: Type of this condition. At most one of each condition
|
||||
type may apply to a resource at any point in time.
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
|
|
@ -84,19 +84,6 @@ var _ = Describe("Test Helm schematic appfile", func() {
|
|||
"replicas": float64(10),
|
||||
},
|
||||
engine: definition.NewTraitAbstractEngine("scaler", pd),
|
||||
Template: `
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *1 | int
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
FullTemplate: &Template{
|
||||
|
|
@ -140,24 +127,6 @@ var _ = Describe("Test Helm schematic appfile", func() {
|
|||
"app.oam.dev/name": appName,
|
||||
"app.oam.dev/appRevision": appName + "-v1",
|
||||
}}}},
|
||||
Traits: []*unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "core.oam.dev/v1alpha2",
|
||||
"kind": "ManualScalerTrait",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app.oam.dev/component": compName,
|
||||
"app.oam.dev/name": appName,
|
||||
"trait.oam.dev/type": "scaler",
|
||||
"trait.oam.dev/resource": "scaler",
|
||||
"app.oam.dev/appRevision": appName + "-v1",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{"replicaCount": int64(10)},
|
||||
},
|
||||
},
|
||||
},
|
||||
PackagedWorkloadResources: []*unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
|
|
@ -258,19 +227,6 @@ spec:
|
|||
"replicas": float64(10),
|
||||
},
|
||||
engine: definition.NewTraitAbstractEngine("scaler", pd),
|
||||
Template: `
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *1 | int
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
FullTemplate: &Template{
|
||||
|
|
@ -328,24 +284,6 @@ spec:
|
|||
expectCompManifest := &oamtypes.ComponentManifest{
|
||||
Name: compName,
|
||||
StandardWorkload: expectWorkload,
|
||||
Traits: []*unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "core.oam.dev/v1alpha2",
|
||||
"kind": "ManualScalerTrait",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app.oam.dev/component": compName,
|
||||
"app.oam.dev/name": appName,
|
||||
"app.oam.dev/appRevision": appName + "-v1",
|
||||
"trait.oam.dev/type": "scaler",
|
||||
"trait.oam.dev/resource": "scaler",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{"replicaCount": int64(10)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Verify expected Component")
|
||||
diff := cmp.Diff(comps[0], expectCompManifest)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ var _ = Describe("Test Live-Diff", func() {
|
|||
ContainSubstring("Component (myweb-1) has been modified(*)"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/service) has been modified(*)"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/ingress) has been modified(*)"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myscaler/scaler) has been removed(-)"),
|
||||
ContainSubstring("Component (myweb-2) has no change"),
|
||||
ContainSubstring("Component (myweb-2) / Trait (myingress/service) has been added(+)"),
|
||||
ContainSubstring("Component (myweb-2) / Trait (myingress/ingress) has been added(+)"),
|
||||
|
|
@ -94,7 +93,6 @@ var _ = Describe("Test Live-Diff", func() {
|
|||
ContainSubstring("Component (myweb-1) has no change"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/service) has no change"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/ingress) has no change"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myscaler/scaler) has no change"),
|
||||
ContainSubstring("Component (myweb-2) has no change"),
|
||||
))
|
||||
Expect(diffResultStr).ShouldNot(SatisfyAny(
|
||||
|
|
@ -112,7 +110,6 @@ var _ = Describe("Test Live-Diff", func() {
|
|||
ContainSubstring("Component (myweb-1) has no change"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/service) has no change"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/ingress) has no change"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myscaler/scaler) has no change"),
|
||||
ContainSubstring("Component (myweb-2) has no change"),
|
||||
ContainSubstring("Component (myweb-2) / Trait (myingress/service) has been added"),
|
||||
ContainSubstring("Component (myweb-2) / Trait (myingress/ingress) has been added"),
|
||||
|
|
@ -133,7 +130,6 @@ var _ = Describe("Test Live-Diff", func() {
|
|||
ContainSubstring("Component (myweb-1) has been modified"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/service) has been modified"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/ingress) has been modified"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myscaler/scaler) has no change"),
|
||||
ContainSubstring("Component (myweb-2) has no change"),
|
||||
))
|
||||
Expect(diffResultStr).ShouldNot(SatisfyAny(
|
||||
|
|
@ -150,7 +146,6 @@ var _ = Describe("Test Live-Diff", func() {
|
|||
ContainSubstring("Component (myweb-1) has no change"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/service) has no change"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myingress/ingress) has no change"),
|
||||
ContainSubstring("Component (myweb-1) / Trait (myscaler/scaler) has been removed"),
|
||||
ContainSubstring("Component (myweb-2) has been removed"),
|
||||
))
|
||||
Expect(diffResultStr).ShouldNot(SatisfyAny(
|
||||
|
|
@ -194,7 +189,6 @@ var _ = Describe("Test Live-Diff", func() {
|
|||
}
|
||||
Expect(runDiff()).Should(ContainSubstring("\"myworker\" not found"))
|
||||
applyFile("td-myingress.yaml", "vela-system")
|
||||
applyFile("td-myscaler.yaml", "vela-system")
|
||||
applyFile("cd-myworker.yaml", "vela-system")
|
||||
applyFile("wd-deploy.yaml", "vela-system")
|
||||
applyFile("wd-ref-objects.yaml", "vela-system")
|
||||
|
|
|
|||
|
|
@ -91,24 +91,19 @@ var _ = BeforeSuite(func(done Done) {
|
|||
|
||||
By("Prepare capability definitions")
|
||||
myingressYAML := readDataFromFile("./testdata/td-myingress.yaml")
|
||||
myscalerYAML := readDataFromFile("./testdata/td-myscaler.yaml")
|
||||
myworkerYAML := readDataFromFile("./testdata/cd-myworker.yaml")
|
||||
|
||||
myworkerDef, err := oamutil.UnMarshalStringToComponentDefinition(myworkerYAML)
|
||||
Expect(err).Should(BeNil())
|
||||
myingressDef, err := oamutil.UnMarshalStringToTraitDefinition(myingressYAML)
|
||||
Expect(err).Should(BeNil())
|
||||
myscalerDef, err := oamutil.UnMarshalStringToTraitDefinition(myscalerYAML)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
cdMyWorker, err := oamutil.Object2Unstructured(myworkerDef)
|
||||
Expect(err).Should(BeNil())
|
||||
tdMyIngress, err := oamutil.Object2Unstructured(myingressDef)
|
||||
Expect(err).Should(BeNil())
|
||||
tdMyScaler, err := oamutil.Object2Unstructured(myscalerDef)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
dryrunOpt = NewDryRunOption(k8sClient, cfg, dm, pd, []oam.Object{cdMyWorker, tdMyIngress, tdMyScaler}, false)
|
||||
dryrunOpt = NewDryRunOption(k8sClient, cfg, dm, pd, []oam.Object{cdMyWorker, tdMyIngress}, false)
|
||||
diffOpt = &LiveDiffOption{DryRun: dryrunOpt, Parser: appfile.NewApplicationParser(k8sClient, dm, pd)}
|
||||
|
||||
close(done)
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ spec:
|
|||
http:
|
||||
/: 80
|
||||
type: myingress
|
||||
- properties:
|
||||
replicas: 2
|
||||
type: myscaler
|
||||
type: myworker
|
||||
- name: myweb-2
|
||||
properties:
|
||||
|
|
@ -90,20 +87,6 @@ spec:
|
|||
serviceName: myweb-1
|
||||
servicePort: 80
|
||||
path: /
|
||||
- trait:
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: ManualScalerTrait
|
||||
metadata:
|
||||
labels:
|
||||
app.oam.dev/appRevision: livediff-demo-v1
|
||||
app.oam.dev/component: myweb-1
|
||||
app.oam.dev/name: livediff-demo
|
||||
trait.oam.dev/resource: scaler
|
||||
trait.oam.dev/type: myscaler
|
||||
name: myweb-1-myscaler-69ccc5855d
|
||||
namespace: default
|
||||
spec:
|
||||
replicaCount: 2
|
||||
- componentName: myweb-2
|
||||
revisionName: myweb-2-v1
|
||||
status:
|
||||
|
|
@ -230,20 +213,3 @@ spec:
|
|||
{\n\t\t\t\t\t\tpath: k\n\t\t\t\t\t\tbackend: {\n\t\t\t\t\t\t\tserviceName:
|
||||
context.name\n\t\t\t\t\t\t\tservicePort: v\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t}\n\t\t}]\n\t}\n}\n"
|
||||
status: {}
|
||||
myscaler:
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata: {}
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
schematic:
|
||||
cue:
|
||||
template: "outputs: scaler: {\n\tapiVersion: \"core.oam.dev/v1alpha2\"\n\tkind:
|
||||
\ \"ManualScalerTrait\"\n\tspec: {\n\t\treplicaCount: parameter.replicas\n\t}\n}\nparameter:
|
||||
{\n\t//+short=r\n\t//+usage=Replicas of the workload\n\treplicas: *1
|
||||
| int\n}\n"
|
||||
workloadRefPath: spec.workloadRef
|
||||
status: {}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ spec:
|
|||
domain: "www.example.com"
|
||||
http:
|
||||
"/": 90 # change a trait
|
||||
# - type: myscaler # remove a trait
|
||||
# properties:
|
||||
# replicas: 2
|
||||
- name: myweb-2
|
||||
type: myworker
|
||||
properties: # no change on component property
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ spec:
|
|||
domain: "www.example.com"
|
||||
http:
|
||||
"/": 80
|
||||
- type: myscaler
|
||||
properties:
|
||||
replicas: 2
|
||||
- name: myweb-2
|
||||
type: myworker
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -19,10 +19,7 @@ spec:
|
|||
properties:
|
||||
domain: "www.example.com"
|
||||
http:
|
||||
"/": 80
|
||||
- type: myscaler
|
||||
properties:
|
||||
replicas: 2
|
||||
"/": 80
|
||||
- name: myweb-2
|
||||
type: myworker
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ spec:
|
|||
domain: "www.example.com"
|
||||
http:
|
||||
"/": 90 # change a trait
|
||||
- type: myscaler
|
||||
properties:
|
||||
replicas: 2
|
||||
- name: myweb-2
|
||||
type: myworker
|
||||
properties: # no change on component property
|
||||
|
|
|
|||
|
|
@ -19,10 +19,7 @@ spec:
|
|||
properties:
|
||||
domain: "www.example.com"
|
||||
http:
|
||||
"/": 80
|
||||
# - type: myscaler # remove a trait
|
||||
# properties:
|
||||
# replicas: 2
|
||||
"/": 80
|
||||
# - name: myweb-2 # remove a component
|
||||
# type: myworker
|
||||
# properties:
|
||||
|
|
|
|||
|
|
@ -20,9 +20,6 @@ spec:
|
|||
domain: "www.example.com"
|
||||
http:
|
||||
"/": 80
|
||||
- type: myscaler
|
||||
properties:
|
||||
replicas: 2
|
||||
- name: livediff-demo
|
||||
type: ref-objects
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Configures replicas for your service."
|
||||
name: myscaler
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
//+usage=Replicas of the workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
|
||||
|
|
@ -23,21 +23,16 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/test"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
errors2 "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/definition"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
common2 "github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
|
@ -93,27 +88,6 @@ var expectedExceptApp = &Appfile{
|
|||
cmd?: [...string]
|
||||
}`,
|
||||
},
|
||||
Traits: []*Trait{
|
||||
{
|
||||
Name: "scaler",
|
||||
Params: map[string]interface{}{
|
||||
"replicas": float64(10),
|
||||
},
|
||||
Template: `
|
||||
outputs:scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *1 | int
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
WorkflowSteps: []workflowv1alpha1.WorkflowStep{
|
||||
|
|
@ -126,34 +100,7 @@ var expectedExceptApp = &Appfile{
|
|||
},
|
||||
}
|
||||
|
||||
const traitDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
name: scaler
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
extension:
|
||||
template: |-
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *1 | int
|
||||
}`
|
||||
|
||||
const componenetDefinition = `
|
||||
const componentDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
|
|
@ -247,10 +194,6 @@ spec:
|
|||
cmd:
|
||||
- sleep
|
||||
- "1000"
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 10
|
||||
workflow:
|
||||
steps:
|
||||
- name: "suspend"
|
||||
|
|
@ -286,7 +229,7 @@ spec:
|
|||
`
|
||||
|
||||
var _ = Describe("Test application parser", func() {
|
||||
It("Test we can parse an application to an appFile", func() {
|
||||
It("Test parse an application", func() {
|
||||
o := v1beta1.Application{}
|
||||
err := yaml.Unmarshal([]byte(appfileYaml), &o)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
|
|
@ -299,17 +242,11 @@ var _ = Describe("Test application parser", func() {
|
|||
}
|
||||
switch o := obj.(type) {
|
||||
case *v1beta1.ComponentDefinition:
|
||||
wd, err := util.UnMarshalStringToComponentDefinition(componenetDefinition)
|
||||
wd, err := util.UnMarshalStringToComponentDefinition(componentDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*o = *wd
|
||||
case *v1beta1.TraitDefinition:
|
||||
td, err := util.UnMarshalStringToTraitDefinition(traitDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*o = *td
|
||||
case *v1beta1.PolicyDefinition:
|
||||
ppd, err := util.UnMarshalStringToPolicyDefinition(policyDefinition)
|
||||
if err != nil {
|
||||
|
|
@ -369,202 +306,6 @@ func equal(af, dest *Appfile) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
var _ = Describe("Test appFile parser", func() {
|
||||
It("application without-trait will only create appfile with workload", func() {
|
||||
// TestApp is test data
|
||||
var TestApp = &Appfile{
|
||||
AppRevisionName: "test-v1",
|
||||
Name: "test",
|
||||
Namespace: "default",
|
||||
RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
|
||||
"scaler": {
|
||||
Spec: v1beta1.TraitDefinitionSpec{},
|
||||
},
|
||||
},
|
||||
Workloads: []*Workload{
|
||||
{
|
||||
Name: "myweb",
|
||||
Type: "worker",
|
||||
Params: map[string]interface{}{
|
||||
"image": "busybox",
|
||||
"cmd": []interface{}{"sleep", "1000"},
|
||||
},
|
||||
Scopes: []Scope{
|
||||
{Name: "test-scope", GVK: metav1.GroupVersionKind{
|
||||
Group: "core.oam.dev",
|
||||
Version: "v1alpha2",
|
||||
Kind: "HealthScope",
|
||||
}},
|
||||
},
|
||||
engine: definition.NewWorkloadAbstractEngine("myweb", pd),
|
||||
FullTemplate: &Template{
|
||||
TemplateStr: `
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: {
|
||||
selector: matchLabels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
template: {
|
||||
metadata: labels: {
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
|
||||
spec: {
|
||||
containers: [{
|
||||
name: context.name
|
||||
image: parameter.image
|
||||
|
||||
if parameter["cmd"] != _|_ {
|
||||
command: parameter.cmd
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
selector:
|
||||
matchLabels:
|
||||
"app.oam.dev/component": context.name
|
||||
}
|
||||
}
|
||||
|
||||
parameter: {
|
||||
// +usage=Which image would you like to use for your service
|
||||
// +short=i
|
||||
image: string
|
||||
|
||||
cmd?: [...string]
|
||||
}`,
|
||||
},
|
||||
Traits: []*Trait{
|
||||
{
|
||||
Name: "scaler",
|
||||
Params: map[string]interface{}{
|
||||
"replicas": float64(10),
|
||||
},
|
||||
engine: definition.NewTraitAbstractEngine("scaler", pd),
|
||||
Template: `
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *1 | int
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
cm := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "kubevela-test-myweb-myconfig", Namespace: "default"},
|
||||
Data: map[string]string{"c1": "v1", "c2": "v2"},
|
||||
}
|
||||
Expect(k8sClient.Create(context.Background(), cm.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
comps, err := TestApp.GenerateComponentManifests()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
expectWorkload := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "myweb",
|
||||
"namespace": "default",
|
||||
"labels": map[string]interface{}{
|
||||
"workload.oam.dev/type": "worker",
|
||||
"app.oam.dev/component": "myweb",
|
||||
"app.oam.dev/appRevision": "test-v1",
|
||||
"app.oam.dev/name": "test",
|
||||
"app.oam.dev/namespace": "default",
|
||||
"app.oam.dev/resourceType": "WORKLOAD",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app.oam.dev/component": "myweb"}},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{"labels": map[string]interface{}{"app.oam.dev/component": "myweb"}},
|
||||
"spec": map[string]interface{}{
|
||||
"containers": []interface{}{
|
||||
map[string]interface{}{
|
||||
"command": []interface{}{"sleep", "1000"},
|
||||
"image": "busybox",
|
||||
"name": "myweb",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectCompManifest := &types.ComponentManifest{
|
||||
Name: "myweb",
|
||||
StandardWorkload: expectWorkload,
|
||||
Traits: []*unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "core.oam.dev/v1alpha2",
|
||||
"kind": "ManualScalerTrait",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "myweb-scaler-5c7695d6c7",
|
||||
"namespace": "default",
|
||||
"labels": map[string]interface{}{
|
||||
"app.oam.dev/component": "myweb",
|
||||
"app.oam.dev/appRevision": "test-v1",
|
||||
"app.oam.dev/name": "test",
|
||||
"app.oam.dev/namespace": "default",
|
||||
"trait.oam.dev/type": "scaler",
|
||||
"trait.oam.dev/resource": "scaler",
|
||||
"app.oam.dev/resourceType": "TRAIT",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{"replicaCount": int64(10)},
|
||||
},
|
||||
},
|
||||
},
|
||||
Scopes: []*corev1.ObjectReference{
|
||||
{
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
Kind: "HealthScope",
|
||||
Name: "test-scope",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// assertion util cannot compare slices embedded in map correctly while slice order is not required
|
||||
// e.g., .containers[0].env in this case
|
||||
// as a workaround, prepare two expected targets covering all possible slice order
|
||||
// if any one is satisfied, the equal assertion pass
|
||||
expectWorkloadOptional := expectWorkload.DeepCopy()
|
||||
unstructured.SetNestedSlice(expectWorkloadOptional.Object, []interface{}{
|
||||
map[string]interface{}{
|
||||
"command": []interface{}{"sleep", "1000"},
|
||||
"image": "busybox",
|
||||
"name": "myweb",
|
||||
},
|
||||
}, "spec", "template", "spec", "containers")
|
||||
|
||||
By(" built components' length must be 1")
|
||||
Expect(len(comps)).To(BeEquivalentTo(1))
|
||||
comp := comps[0]
|
||||
Expect(comp.Name).Should(Equal(expectCompManifest.Name))
|
||||
Expect(cmp.Diff(comp.Traits, expectCompManifest.Traits)).Should(BeEmpty())
|
||||
Expect(comp.Scopes).Should(Equal(expectCompManifest.Scopes))
|
||||
Expect(cmp.Diff(comp.StandardWorkload, expectWorkloadOptional)).Should(BeEmpty())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
var _ = Describe("Test application parser", func() {
|
||||
var app v1beta1.Application
|
||||
var apprev v1beta1.ApplicationRevision
|
||||
|
|
@ -664,17 +405,11 @@ patch: spec: replicas: parameter.replicas
|
|||
}
|
||||
switch o := obj.(type) {
|
||||
case *v1beta1.ComponentDefinition:
|
||||
wd, err := util.UnMarshalStringToComponentDefinition(componenetDefinition)
|
||||
wd, err := util.UnMarshalStringToComponentDefinition(componentDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*o = *wd
|
||||
case *v1beta1.TraitDefinition:
|
||||
td, err := util.UnMarshalStringToTraitDefinition(traitDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*o = *td
|
||||
case *v1beta1.WorkflowStepDefinition:
|
||||
*o = wsd
|
||||
case *v1beta1.ApplicationRevision:
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ const (
|
|||
DisableAllCaps = "all"
|
||||
// DisableNoneCaps disable none of capabilities
|
||||
DisableNoneCaps = ""
|
||||
// ManualScalerTraitControllerName is the controller name of manual scaler trait
|
||||
ManualScalerTraitControllerName = "manualscalertrait"
|
||||
// HealthScopeControllerName is the controller name of healthScope controller
|
||||
HealthScopeControllerName = "healthscope"
|
||||
// RolloutControllerName is the controller name of rollout controller
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -29,7 +29,6 @@ import (
|
|||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
|
||||
|
|
@ -55,9 +54,6 @@ var _ = Describe("Test application controller finalizer logic", func() {
|
|||
badCD := &v1beta1.ComponentDefinition{}
|
||||
badCDJson, _ := yaml.YAMLToJSON([]byte(badCompDefYaml))
|
||||
|
||||
td := &v1beta1.TraitDefinition{}
|
||||
tdDefJson, _ := yaml.YAMLToJSON([]byte(crossNsTdYaml))
|
||||
|
||||
BeforeEach(func() {
|
||||
ns := v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
|
@ -69,9 +65,6 @@ var _ = Describe("Test application controller finalizer logic", func() {
|
|||
Expect(json.Unmarshal(cDDefJson, cd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, cd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
Expect(json.Unmarshal(tdDefJson, td)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, td.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
Expect(json.Unmarshal(ncdDefJson, ncd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, ncd.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
|
|
@ -82,60 +75,9 @@ var _ = Describe("Test application controller finalizer logic", func() {
|
|||
AfterEach(func() {
|
||||
By("[TEST] Clean up resources after an integration test")
|
||||
Expect(k8sClient.DeleteAllOf(ctx, &appsv1.Deployment{}, client.InNamespace(namespace)))
|
||||
Expect(k8sClient.DeleteAllOf(ctx, &v1alpha2.ManualScalerTrait{}, client.InNamespace(namespace)))
|
||||
Expect(k8sClient.DeleteAllOf(ctx, &appsv1.ControllerRevision{}, client.InNamespace(namespace)))
|
||||
})
|
||||
|
||||
It("Test component have normal workload", func() {
|
||||
appName := "app-1"
|
||||
appKey := types.NamespacedName{Namespace: namespace, Name: appName}
|
||||
app := getApp(appName, namespace, "normal-worker")
|
||||
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
|
||||
|
||||
By("Create a normal workload app")
|
||||
checkApp := &v1beta1.Application{}
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey})
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(checkApp.Status.Phase).Should(Equal(common.ApplicationRunning))
|
||||
Expect(len(checkApp.Finalizers)).Should(BeEquivalentTo(1))
|
||||
|
||||
rt := &v1beta1.ResourceTracker{}
|
||||
Expect(k8sClient.Get(ctx, getTrackerKey(checkApp.Namespace, checkApp.Name, "v1"), rt)).Should(Succeed())
|
||||
|
||||
By("add a cross namespace trait for application")
|
||||
updateApp := checkApp.DeepCopy()
|
||||
updateApp.Spec.Components[0].Traits = []common.ApplicationTrait{
|
||||
{
|
||||
Type: "cross-scaler",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"replicas": 1}`)},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Update(ctx, updateApp)).Should(BeNil())
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey})
|
||||
checkApp = new(v1beta1.Application)
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, getTrackerKey(checkApp.Namespace, checkApp.Name, "v2"), rt)).Should(BeNil())
|
||||
Expect(len(checkApp.Finalizers)).Should(BeEquivalentTo(1))
|
||||
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey})
|
||||
checkApp = new(v1beta1.Application)
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, getTrackerKey(checkApp.Namespace, checkApp.Name, "v2"), rt)).Should(BeNil())
|
||||
Expect(len(checkApp.Finalizers)).Should(BeEquivalentTo(1))
|
||||
Expect(checkApp.Finalizers[0]).Should(BeEquivalentTo(resourceTrackerFinalizer))
|
||||
|
||||
By("update app to delete cross namespace trait")
|
||||
checkApp = &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
updateApp = checkApp.DeepCopy()
|
||||
updateApp.Spec.Components[0].Traits = nil
|
||||
Expect(k8sClient.Update(ctx, updateApp)).Should(BeNil())
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey})
|
||||
checkApp = new(v1beta1.Application)
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, getTrackerKey(checkApp.Namespace, checkApp.Name, "v3"), rt)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test error occurs in the middle of dispatching", func() {
|
||||
appName := "bad-app"
|
||||
appKey := types.NamespacedName{Namespace: namespace, Name: appName}
|
||||
|
|
@ -226,45 +168,6 @@ var _ = Describe("Test application controller finalizer logic", func() {
|
|||
Expect(k8sClient.Delete(ctx, checkApp)).Should(BeNil())
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey})
|
||||
})
|
||||
|
||||
It("Test cross namespace workload and trait, then update the app to delete trait ", func() {
|
||||
appName := "app-4"
|
||||
appKey := types.NamespacedName{Namespace: namespace, Name: appName}
|
||||
app := getApp(appName, namespace, "cross-worker")
|
||||
app.Spec.Components[0].Traits = []common.ApplicationTrait{
|
||||
{
|
||||
Type: "cross-scaler",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"replicas": 1}`)},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, app)).Should(BeNil())
|
||||
By("Create a cross workload trait app")
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey})
|
||||
checkApp := &v1beta1.Application{}
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(checkApp.Status.Phase).Should(Equal(common.ApplicationRunning))
|
||||
Expect(len(checkApp.Finalizers)).Should(BeEquivalentTo(1))
|
||||
rt := &v1beta1.ResourceTracker{}
|
||||
Expect(k8sClient.Get(ctx, getTrackerKey(checkApp.Namespace, checkApp.Name, "v1"), rt)).Should(BeNil())
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey})
|
||||
checkApp = new(v1beta1.Application)
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(len(checkApp.Finalizers)).Should(BeEquivalentTo(1))
|
||||
Expect(checkApp.Finalizers[0]).Should(BeEquivalentTo(resourceTrackerFinalizer))
|
||||
Expect(len(rt.Spec.ManagedResources)).Should(BeEquivalentTo(2))
|
||||
By("Update the app, set type to normal-worker")
|
||||
checkApp.Spec.Components[0].Traits = nil
|
||||
Expect(k8sClient.Update(ctx, checkApp)).Should(BeNil())
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey})
|
||||
rt = &v1beta1.ResourceTracker{}
|
||||
checkApp = new(v1beta1.Application)
|
||||
Expect(k8sClient.Get(ctx, appKey, checkApp)).Should(BeNil())
|
||||
Expect(k8sClient.Get(ctx, getTrackerKey(checkApp.Namespace, checkApp.Name, "v2"), rt)).Should(BeNil())
|
||||
Expect(len(rt.Spec.ManagedResources)).Should(BeEquivalentTo(1))
|
||||
Expect(k8sClient.Delete(ctx, checkApp)).Should(BeNil())
|
||||
testutil.ReconcileOnceAfterFinalizer(reconciler, ctrl.Request{NamespacedName: appKey})
|
||||
Expect(k8sClient.Get(ctx, getTrackerKey(checkApp.Namespace, checkApp.Name, "v2"), rt)).Should(util.NotFoundMatcher{})
|
||||
})
|
||||
})
|
||||
|
||||
func getApp(appName, namespace, comptype string) *v1beta1.Application {
|
||||
|
|
@ -351,38 +254,6 @@ spec:
|
|||
}
|
||||
`
|
||||
|
||||
crossNsTdYaml = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
name: cross-scaler
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
extension:
|
||||
template: |-
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
metadata: {
|
||||
namespace: "cross-namespace"
|
||||
}
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *1 | int
|
||||
}
|
||||
`
|
||||
|
||||
normalCompDefYaml = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
|
|
|
|||
|
|
@ -57,9 +57,6 @@ var _ = Describe("Test Assemble Options", func() {
|
|||
domain: localhost
|
||||
http:
|
||||
"/": 8000
|
||||
- type: manualscaler
|
||||
properties:
|
||||
replicas: 3
|
||||
*/
|
||||
Expect(err).Should(BeNil())
|
||||
err = yaml.Unmarshal(b, appRev)
|
||||
|
|
@ -72,11 +69,11 @@ var _ = Describe("Test Assemble Options", func() {
|
|||
By("Verify amount of result resources")
|
||||
allResources, err := ao.AssembledManifests()
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(allResources)).Should(Equal(4))
|
||||
Expect(len(allResources)).Should(Equal(3))
|
||||
|
||||
By("Verify amount of result grouped resources")
|
||||
Expect(len(workloads)).Should(Equal(1))
|
||||
Expect(len(traits[compName])).Should(Equal(3))
|
||||
Expect(len(traits[compName])).Should(Equal(2))
|
||||
|
||||
By("Verify workload metadata (name, namespace, labels, annotations, ownerRef)")
|
||||
wl := workloads[compName]
|
||||
|
|
@ -116,18 +113,6 @@ var _ = Describe("Test Assemble Options", func() {
|
|||
oam.LabelOAMResourceType))
|
||||
Expect(len(wl.GetAnnotations())).Should(Equal(1))
|
||||
|
||||
By("Verify set workload reference to trait")
|
||||
scaler := traits[compName][2]
|
||||
wlRef, found, err := unstructured.NestedMap(scaler.Object, "spec", "workloadRef")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(found).Should(BeTrue())
|
||||
wantWorkloadRef := map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"name": compName,
|
||||
}
|
||||
Expect(wlRef).Should(Equal(wantWorkloadRef))
|
||||
|
||||
By("Verify referenced scopes")
|
||||
scopes, err := ao.ReferencedScopes()
|
||||
Expect(err).Should(BeNil())
|
||||
|
|
|
|||
|
|
@ -33,9 +33,6 @@ spec:
|
|||
http:
|
||||
/: 8000
|
||||
type: ingress
|
||||
- properties:
|
||||
replicas: 3
|
||||
type: manualscaler
|
||||
status: {}
|
||||
componentDefinitions:
|
||||
webservice:
|
||||
|
|
@ -202,30 +199,3 @@ spec:
|
|||
healthPolicy: |
|
||||
isHealth: len(context.outputs.service.spec.clusterIP) > 0
|
||||
status: {}
|
||||
manualscaler:
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata: {}
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
podDisruptive: true
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
//+usage=Replicas of the workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
workloadRefPath: spec.workloadRef
|
||||
status: {}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ var _ = Describe("test generate revision ", func() {
|
|||
cd := v1beta1.ComponentDefinition{}
|
||||
webCompDef := v1beta1.ComponentDefinition{}
|
||||
wd := v1beta1.WorkloadDefinition{}
|
||||
td := v1beta1.TraitDefinition{}
|
||||
sd := v1beta1.ScopeDefinition{}
|
||||
rolloutTd := v1beta1.TraitDefinition{}
|
||||
var handler *AppHandler
|
||||
|
|
@ -76,11 +75,6 @@ var _ = Describe("test generate revision ", func() {
|
|||
cd.ResourceVersion = ""
|
||||
Expect(k8sClient.Create(ctx, &cd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
traitDefJson, _ := yaml.YAMLToJSON([]byte(traitDefYaml))
|
||||
Expect(json.Unmarshal(traitDefJson, &td)).Should(BeNil())
|
||||
td.ResourceVersion = ""
|
||||
Expect(k8sClient.Create(ctx, &td)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
scopeDefJson, _ := yaml.YAMLToJSON([]byte(scopeDefYaml))
|
||||
Expect(json.Unmarshal(scopeDefJson, &sd)).Should(BeNil())
|
||||
sd.ResourceVersion = ""
|
||||
|
|
@ -123,14 +117,6 @@ var _ = Describe("test generate revision ", func() {
|
|||
Properties: &runtime.RawExtension{
|
||||
Raw: []byte(`{"image": "oamdev/testapp:v1", "cmd": ["node", "server.js"]}`),
|
||||
},
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: td.Name,
|
||||
Properties: &runtime.RawExtension{
|
||||
Raw: []byte(`{"replicas": 5}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -152,7 +138,7 @@ var _ = Describe("test generate revision ", func() {
|
|||
appRevision1.Spec.Application = app
|
||||
appRevision1.Spec.ComponentDefinitions[cd.Name] = cd
|
||||
appRevision1.Spec.WorkloadDefinitions[wd.Name] = wd
|
||||
appRevision1.Spec.TraitDefinitions[td.Name] = td
|
||||
appRevision1.Spec.TraitDefinitions[rolloutTd.Name] = rolloutTd
|
||||
appRevision1.Spec.ScopeDefinitions[sd.Name] = sd
|
||||
|
||||
appRevision2 = *appRevision1.DeepCopy()
|
||||
|
|
@ -199,9 +185,6 @@ var _ = Describe("test generate revision ", func() {
|
|||
// add an annotation to workload Definition
|
||||
wd.SetAnnotations(map[string]string{oam.AnnotationAppRollout: "true"})
|
||||
appRevision2.Spec.WorkloadDefinitions[wd.Name] = wd
|
||||
// add status to td
|
||||
td.SetConditions(v1alpha1.NewPositiveCondition("Test"))
|
||||
appRevision2.Spec.TraitDefinitions[td.Name] = td
|
||||
// change the cd meta
|
||||
cd.ClusterName = "testCluster"
|
||||
appRevision2.Spec.ComponentDefinitions[cd.Name] = cd
|
||||
|
|
@ -209,14 +192,6 @@ var _ = Describe("test generate revision ", func() {
|
|||
verifyEqual()
|
||||
})
|
||||
|
||||
It("Test app revisions with different trait spec should produce different hash and not equal", func() {
|
||||
// change td spec
|
||||
td.Spec.AppliesToWorkloads = append(td.Spec.AppliesToWorkloads, "allWorkload")
|
||||
appRevision2.Spec.TraitDefinitions[td.Name] = td
|
||||
|
||||
verifyNotEqual()
|
||||
})
|
||||
|
||||
It("Test app revisions with different application spec should produce different hash and not equal", func() {
|
||||
// change application setting
|
||||
appRevision2.Spec.Application.Spec.Components[0].Properties.Raw =
|
||||
|
|
|
|||
43
pkg/controller/core.oam.dev/v1alpha2/application/testdata/definitions/hubcpuscaler.yaml
vendored
Normal file
43
pkg/controller/core.oam.dev/v1alpha2/application/testdata/definitions/hubcpuscaler.yaml
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Automatically scale the component based on CPU usage.
|
||||
labels:
|
||||
custom.definition.oam.dev/ui-hidden: "true"
|
||||
name: hubcpuscaler
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
controlPlaneOnly: true
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: hubcpuscaler: {
|
||||
apiVersion: "autoscaling/v1"
|
||||
kind: "HorizontalPodAutoscaler"
|
||||
metadata: name: context.name
|
||||
spec: {
|
||||
scaleTargetRef: {
|
||||
apiVersion: parameter.targetAPIVersion
|
||||
kind: parameter.targetKind
|
||||
name: context.name
|
||||
}
|
||||
minReplicas: parameter.min
|
||||
maxReplicas: parameter.max
|
||||
targetCPUUtilizationPercentage: parameter.cpuUtil
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
// +usage=Specify the minimal number of replicas to which the autoscaler can scale down
|
||||
min: *1 | int
|
||||
// +usage=Specify the maximum number of of replicas to which the autoscaler can scale up
|
||||
max: *10 | int
|
||||
// +usage=Specify the average CPU utilization, for example, 50 means the CPU usage is 50%
|
||||
cpuUtil: *50 | int
|
||||
// +usage=Specify the apiVersion of scale target
|
||||
targetAPIVersion: *"apps/v1" | string
|
||||
// +usage=Specify the kind of scale target
|
||||
targetKind: *"Deployment" | string
|
||||
}
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package applicationconfiguration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/testutil"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Test Deploying ApplicationConfiguration without TraitDefinition", func() {
|
||||
const (
|
||||
namespace = "definition-test"
|
||||
appName = "hello"
|
||||
componentName = "backend"
|
||||
)
|
||||
var (
|
||||
ctx = context.Background()
|
||||
workload appsv1.Deployment
|
||||
component v1alpha2.Component
|
||||
workloadKey = client.ObjectKey{
|
||||
Name: componentName,
|
||||
Namespace: namespace,
|
||||
}
|
||||
appConfig v1alpha2.ApplicationConfiguration
|
||||
appConfigKey = client.ObjectKey{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
}
|
||||
req = reconcile.Request{NamespacedName: appConfigKey}
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {})
|
||||
|
||||
It("ManualScalerTrait should work successfully even though its TraitDefinition doesn't exist", func() {
|
||||
var componentStr = `
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: Component
|
||||
metadata:
|
||||
name: backend
|
||||
namespace: definition-test
|
||||
spec:
|
||||
workload:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: backend
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.9.4
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: nginx
|
||||
env:
|
||||
- name: TEST_ENV
|
||||
value: test
|
||||
command: [ "/bin/bash", "-c", "--" ]
|
||||
args: [ "while true; do sleep 30; done;" ]
|
||||
`
|
||||
|
||||
var appConfigStr = `
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: ApplicationConfiguration
|
||||
metadata:
|
||||
name: hello
|
||||
namespace: definition-test
|
||||
spec:
|
||||
components:
|
||||
- componentName: backend
|
||||
traits:
|
||||
- trait:
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: ManualScalerTrait
|
||||
spec:
|
||||
replicaCount: 2
|
||||
workloadRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: backend
|
||||
`
|
||||
|
||||
By("Create namespace")
|
||||
Eventually(
|
||||
func() error {
|
||||
return k8sClient.Create(ctx, &ns)
|
||||
},
|
||||
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("Create Component")
|
||||
Expect(yaml.Unmarshal([]byte(componentStr), &component)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &component)).Should(Succeed())
|
||||
cmpV1 := &v1alpha2.Component{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cmpV1)).Should(Succeed())
|
||||
|
||||
By("Create ApplicationConfiguration")
|
||||
Expect(yaml.Unmarshal([]byte(appConfigStr), &appConfig)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &appConfig)).Should(Succeed())
|
||||
By("Check AppConfig created successfully")
|
||||
ac := &v1alpha2.ApplicationConfiguration{}
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: appName}, ac)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeNil())
|
||||
|
||||
By("Check workload created successfully")
|
||||
Eventually(func() error {
|
||||
By("Reconcile")
|
||||
testutil.ReconcileRetry(reconciler, req)
|
||||
return k8sClient.Get(ctx, workloadKey, &workload)
|
||||
}, 5*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
By("Check reconcile again and no error will happen")
|
||||
testutil.ReconcileRetry(reconciler, req)
|
||||
|
||||
By("Check appConfig condition should not have error")
|
||||
Eventually(func() string {
|
||||
By("Reconcile again and should not have error")
|
||||
testutil.ReconcileRetry(reconciler, req)
|
||||
err := k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if len(appConfig.Status.Conditions) != 1 {
|
||||
return "condition len should be 1 but now is " + strconv.Itoa(len(appConfig.Status.Conditions))
|
||||
}
|
||||
return string(appConfig.Status.Conditions[0].Reason)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeEquivalentTo("ReconcileSuccess"))
|
||||
|
||||
By("Check trait CR is created")
|
||||
var scaleName string
|
||||
scaleList := v1alpha2.ManualScalerTraitList{}
|
||||
labels := &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"app.oam.dev/component": componentName,
|
||||
},
|
||||
}
|
||||
selector, _ := metav1.LabelSelectorAsSelector(labels)
|
||||
err := k8sClient.List(ctx, &scaleList, &client.ListOptions{
|
||||
Namespace: namespace,
|
||||
LabelSelector: selector,
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
traitNamePrefix := fmt.Sprintf("%s-trait-", componentName)
|
||||
var traitExistFlag bool
|
||||
for _, t := range scaleList.Items {
|
||||
if strings.HasPrefix(t.Name, traitNamePrefix) {
|
||||
traitExistFlag = true
|
||||
scaleName = t.Name
|
||||
}
|
||||
}
|
||||
Expect(traitExistFlag).Should(BeTrue())
|
||||
|
||||
By("Update ApplicationConfiguration by changing spec of trait")
|
||||
newTrait := &v1alpha2.ManualScalerTrait{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
Kind: "ManualScalerTrait",
|
||||
},
|
||||
Spec: v1alpha2.ManualScalerTraitSpec{
|
||||
ReplicaCount: 3,
|
||||
WorkloadReference: corev1.ObjectReference{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: componentName,
|
||||
},
|
||||
},
|
||||
}
|
||||
appConfig.Spec.Components[0].Traits = []v1alpha2.ComponentTrait{{Trait: runtime.RawExtension{Object: newTrait.DeepCopyObject()}}}
|
||||
Expect(k8sClient.Update(ctx, &appConfig)).Should(BeNil())
|
||||
|
||||
By("Reconcile")
|
||||
testutil.ReconcileRetry(reconciler, req)
|
||||
|
||||
By("Check again that appConfig condition should not have error")
|
||||
Eventually(func() string {
|
||||
By("Reconcile again and should not have error")
|
||||
testutil.ReconcileRetry(reconciler, req)
|
||||
err := k8sClient.Get(ctx, appConfigKey, &appConfig)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if len(appConfig.Status.Conditions) != 1 {
|
||||
return "condition len should be 1 but now is " + strconv.Itoa(len(appConfig.Status.Conditions))
|
||||
}
|
||||
return string(appConfig.Status.Conditions[0].Reason)
|
||||
}, 3*time.Second, 300*time.Millisecond).Should(BeEquivalentTo("ReconcileSuccess"))
|
||||
|
||||
By("Check new trait CR is applied")
|
||||
scale := v1alpha2.ManualScalerTrait{}
|
||||
scaleKey := client.ObjectKey{Name: scaleName, Namespace: namespace}
|
||||
Eventually(func() int32 {
|
||||
By("Reconcile")
|
||||
testutil.ReconcileRetry(reconciler, req)
|
||||
if err := k8sClient.Get(ctx, scaleKey, &scale); err != nil {
|
||||
return 0
|
||||
}
|
||||
return scale.Spec.ReplicaCount
|
||||
}, 5*time.Second, time.Second).Should(Equal(int32(3)))
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// delete the namespace with all its resources
|
||||
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).
|
||||
Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{}))
|
||||
})
|
||||
})
|
||||
|
|
@ -18,7 +18,6 @@ package applicationconfiguration
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
|
@ -36,11 +35,9 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
core "github.com/oam-dev/kubevela/apis/core.oam.dev"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
|
|
@ -1771,239 +1768,6 @@ func TestSetTraitProperties(t *testing.T) {
|
|||
assert.Equal(t, expU, u)
|
||||
}
|
||||
|
||||
func TestRenderTraitName(t *testing.T) {
|
||||
var scheme = runtime.NewScheme()
|
||||
assert.NoError(t, clientgoscheme.AddToScheme(scheme))
|
||||
assert.NoError(t, core.AddToScheme(scheme))
|
||||
namespace := "ns"
|
||||
acName := "coolappconfig3"
|
||||
acUID := types.UID("definitely-a-uuid")
|
||||
componentName := "component3"
|
||||
|
||||
mts := v1alpha2.ManualScalerTrait{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ManualScalerTraitSpec{
|
||||
ReplicaCount: 3,
|
||||
},
|
||||
}
|
||||
|
||||
gvks, _, _ := scheme.ObjectKinds(&mts)
|
||||
gvk := gvks[0]
|
||||
mts.APIVersion = gvk.GroupVersion().String()
|
||||
mts.Kind = gvk.Kind
|
||||
raw, _ := json.Marshal(mts)
|
||||
|
||||
ac := &v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: acName,
|
||||
UID: acUID,
|
||||
},
|
||||
Spec: v1alpha2.ApplicationConfigurationSpec{
|
||||
Components: []v1alpha2.ApplicationConfigurationComponent{
|
||||
{
|
||||
ComponentName: componentName,
|
||||
Traits: []v1alpha2.ComponentTrait{
|
||||
{
|
||||
Trait: runtime.RawExtension{
|
||||
Object: &mts,
|
||||
Raw: raw,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v1alpha2.ApplicationConfigurationStatus{
|
||||
Workloads: []v1alpha2.WorkloadStatus{
|
||||
{
|
||||
ComponentName: componentName,
|
||||
Traits: []v1alpha2.WorkloadTrait{
|
||||
{
|
||||
Reference: corev1.ObjectReference{
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
Kind: gvk.Kind,
|
||||
Name: "component3-trait-11111111",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
mapResult, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ac.Spec.Components[0].Traits[0].Trait.Object)
|
||||
assert.NoError(t, err)
|
||||
data := unstructured.Unstructured{Object: mapResult}
|
||||
|
||||
traitName := getTraitName(ac, componentName, &ac.Spec.Components[0].Traits[0], &data, &v1alpha2.TraitDefinition{})
|
||||
assert.Equal(t, traitName, "component3-trait-11111111")
|
||||
}
|
||||
|
||||
func TestRenderTraitNameWithoutReferenceName(t *testing.T) {
|
||||
var scheme = runtime.NewScheme()
|
||||
assert.NoError(t, clientgoscheme.AddToScheme(scheme))
|
||||
assert.NoError(t, core.AddToScheme(scheme))
|
||||
namespace := "ns"
|
||||
acName := "coolappconfig4"
|
||||
acUID := types.UID("definitely-a-uuid")
|
||||
componentName := "component4"
|
||||
|
||||
mts := v1alpha2.ManualScalerTrait{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.ManualScalerTraitSpec{
|
||||
ReplicaCount: 3,
|
||||
},
|
||||
}
|
||||
|
||||
gvks, _, _ := scheme.ObjectKinds(&mts)
|
||||
gvk := gvks[0]
|
||||
mts.APIVersion = gvk.GroupVersion().String()
|
||||
mts.Kind = gvk.Kind
|
||||
raw, _ := json.Marshal(mts)
|
||||
|
||||
ac := &v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: acName,
|
||||
UID: acUID,
|
||||
},
|
||||
Spec: v1alpha2.ApplicationConfigurationSpec{
|
||||
Components: []v1alpha2.ApplicationConfigurationComponent{
|
||||
{
|
||||
ComponentName: componentName,
|
||||
Traits: []v1alpha2.ComponentTrait{
|
||||
{
|
||||
Trait: runtime.RawExtension{
|
||||
Object: &mts,
|
||||
Raw: raw,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v1alpha2.ApplicationConfigurationStatus{
|
||||
Workloads: []v1alpha2.WorkloadStatus{
|
||||
{
|
||||
ComponentName: componentName,
|
||||
Traits: []v1alpha2.WorkloadTrait{
|
||||
{
|
||||
Reference: corev1.ObjectReference{
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
Kind: gvk.Kind,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
traitDef := v1alpha2.TraitDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
Kind: "ManualScalerTrait",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "manualscalertraits.core.oam.dev",
|
||||
},
|
||||
}
|
||||
|
||||
mapResult, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ac.Spec.Components[0].Traits[0].Trait.Object)
|
||||
assert.NoError(t, err)
|
||||
data := unstructured.Unstructured{Object: mapResult}
|
||||
|
||||
traitName := getTraitName(ac, componentName, &ac.Spec.Components[0].Traits[0], &data, &traitDef)
|
||||
assert.Contains(t, traitName, "component4-manualscalertraits")
|
||||
}
|
||||
|
||||
func TestRenderTraitNameWithShortNameTraitDefinition(t *testing.T) {
|
||||
var scheme = runtime.NewScheme()
|
||||
assert.NoError(t, clientgoscheme.AddToScheme(scheme))
|
||||
assert.NoError(t, core.AddToScheme(scheme))
|
||||
namespace := "ns"
|
||||
acName := "coolappconfig5"
|
||||
acUID := types.UID("definitely-a-uuid")
|
||||
componentName := "component5"
|
||||
|
||||
mts := v1alpha2.ManualScalerTrait{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Labels: map[string]string{
|
||||
oam.TraitTypeLabel: "scale",
|
||||
},
|
||||
},
|
||||
Spec: v1alpha2.ManualScalerTraitSpec{
|
||||
ReplicaCount: 3,
|
||||
},
|
||||
}
|
||||
|
||||
gvks, _, _ := scheme.ObjectKinds(&mts)
|
||||
gvk := gvks[0]
|
||||
mts.APIVersion = gvk.GroupVersion().String()
|
||||
mts.Kind = gvk.Kind
|
||||
raw, _ := json.Marshal(mts)
|
||||
|
||||
ac := &v1alpha2.ApplicationConfiguration{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: acName,
|
||||
UID: acUID,
|
||||
},
|
||||
Spec: v1alpha2.ApplicationConfigurationSpec{
|
||||
Components: []v1alpha2.ApplicationConfigurationComponent{
|
||||
{
|
||||
ComponentName: componentName,
|
||||
Traits: []v1alpha2.ComponentTrait{
|
||||
{
|
||||
Trait: runtime.RawExtension{
|
||||
Object: &mts,
|
||||
Raw: raw,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: v1alpha2.ApplicationConfigurationStatus{
|
||||
Workloads: []v1alpha2.WorkloadStatus{
|
||||
{
|
||||
ComponentName: componentName,
|
||||
Traits: []v1alpha2.WorkloadTrait{
|
||||
{
|
||||
Reference: corev1.ObjectReference{
|
||||
APIVersion: gvk.GroupVersion().String(),
|
||||
Kind: gvk.Kind,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
traitDef := v1alpha2.TraitDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
Kind: "ManualScalerTrait",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "scale",
|
||||
},
|
||||
}
|
||||
mapResult, err := runtime.DefaultUnstructuredConverter.ToUnstructured(ac.Spec.Components[0].Traits[0].Trait.Object)
|
||||
assert.NoError(t, err)
|
||||
data := unstructured.Unstructured{Object: mapResult}
|
||||
|
||||
traitName := getTraitName(ac, componentName, &ac.Spec.Components[0].Traits[0], &data, &traitDef)
|
||||
assert.Contains(t, traitName, "component5-scale")
|
||||
}
|
||||
|
||||
func TestMatchValue(t *testing.T) {
|
||||
obj := &unstructured.Unstructured{}
|
||||
obj.SetAPIVersion("v1")
|
||||
|
|
|
|||
|
|
@ -1,234 +0,0 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package manualscalertrait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/event"
|
||||
cpmeta "github.com/crossplane/crossplane-runtime/pkg/meta"
|
||||
"github.com/pkg/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/kube-openapi/pkg/util/proto"
|
||||
"k8s.io/kubectl/pkg/explain"
|
||||
"k8s.io/kubectl/pkg/util/openapi"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/condition"
|
||||
oamv1alpha2 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/common"
|
||||
controller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
// Reconcile error strings.
|
||||
const (
|
||||
errQueryOpenAPI = "failed to query openAPI"
|
||||
errPatchTobeScaledResource = "cannot patch the resource for scale"
|
||||
errScaleResource = "cannot scale the resource"
|
||||
)
|
||||
|
||||
// Setup adds a controller that reconciles ContainerizedWorkload.
|
||||
func Setup(mgr ctrl.Manager, args controller.Args) error {
|
||||
reconciler := Reconciler{
|
||||
Client: mgr.GetClient(),
|
||||
DiscoveryClient: *discovery.NewDiscoveryClientForConfigOrDie(mgr.GetConfig()),
|
||||
dm: args.DiscoveryMapper,
|
||||
record: event.NewAPIRecorder(mgr.GetEventRecorderFor("ManualScalarTrait")),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}
|
||||
return reconciler.SetupWithManager(mgr)
|
||||
|
||||
}
|
||||
|
||||
// Reconciler reconciles a ManualScalarTrait object
|
||||
type Reconciler struct {
|
||||
client.Client
|
||||
discovery.DiscoveryClient
|
||||
dm discoverymapper.DiscoveryMapper
|
||||
record event.Recorder
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// Reconcile to reconcile manual trait.
|
||||
// +kubebuilder:rbac:groups=core.oam.dev,resources=manualscalertraits,verbs=get;list;watch
|
||||
// +kubebuilder:rbac:groups=core.oam.dev,resources=manualscalertraits/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=core.oam.dev,resources=workloaddefinition,verbs=get;list;
|
||||
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;update;patch;delete
|
||||
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
ctx, cancel := common.NewReconcileContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
klog.InfoS("Reconcile manualscalar trait", "trait", klog.KRef(req.Namespace, req.Name))
|
||||
|
||||
var manualScalar oamv1alpha2.ManualScalerTrait
|
||||
if err := r.Get(ctx, req.NamespacedName, &manualScalar); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
ctx = util.SetNamespaceInCtx(ctx, manualScalar.Namespace)
|
||||
|
||||
klog.InfoS("Get the manualscalar trait", "ReplicaCount", manualScalar.Spec.ReplicaCount,
|
||||
"Annotations", manualScalar.GetAnnotations())
|
||||
// find the resource object to record the event to, default is the parent appConfig.
|
||||
eventObj, err := util.LocateParentAppConfig(ctx, r.Client, &manualScalar)
|
||||
if eventObj == nil {
|
||||
// fallback to workload itself
|
||||
klog.ErrorS(err, "Failed to find the parent resource", "manualScalar", manualScalar.Name)
|
||||
eventObj = &manualScalar
|
||||
}
|
||||
// Fetch the workload instance this trait is referring to
|
||||
workload, err := util.FetchWorkload(ctx, r.Client, &manualScalar)
|
||||
if err != nil {
|
||||
r.record.Event(eventObj, event.Warning(util.ErrLocateWorkload, err))
|
||||
return ctrl.Result{}, util.EndReconcileWithNegativeCondition(
|
||||
ctx, r, &manualScalar, condition.ReconcileError(errors.Wrap(err, util.ErrLocateWorkload)))
|
||||
}
|
||||
|
||||
// Fetch the child resources list from the corresponding workload
|
||||
resources, err := util.FetchWorkloadChildResources(ctx, r.Client, r.dm, workload)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Error while fetching the workload child resources", "workload", workload.UnstructuredContent())
|
||||
r.record.Event(eventObj, event.Warning(util.ErrFetchChildResources, err))
|
||||
return ctrl.Result{}, util.EndReconcileWithNegativeCondition(ctx, r, &manualScalar,
|
||||
condition.ReconcileError(errors.New(util.ErrFetchChildResources)))
|
||||
}
|
||||
// include the workload itself if there is no child resources
|
||||
if len(resources) == 0 {
|
||||
resources = append(resources, workload)
|
||||
}
|
||||
// Scale the child resources that we know how to scale
|
||||
result, err := r.scaleResources(ctx, manualScalar, resources)
|
||||
// the scaleResources function will patch error message and should return here to prevent the condition override by the following patch.
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
if err != nil {
|
||||
r.record.Event(eventObj, event.Warning(errScaleResource, err))
|
||||
return result, err
|
||||
}
|
||||
r.record.Event(eventObj, event.Normal("Manual scalar applied",
|
||||
fmt.Sprintf("Trait `%s` successfully scaled a resource to %d instances",
|
||||
manualScalar.Name, manualScalar.Spec.ReplicaCount)))
|
||||
return ctrl.Result{}, util.EndReconcileWithPositiveCondition(ctx, r, &manualScalar, condition.ReconcileSuccess())
|
||||
}
|
||||
|
||||
// identify child resources and scale them
|
||||
func (r *Reconciler) scaleResources(ctx context.Context, manualScalar oamv1alpha2.ManualScalerTrait, resources []*unstructured.Unstructured) (ctrl.Result, error) {
|
||||
// scale all the resources that we can scale
|
||||
isController := false
|
||||
bod := true
|
||||
found := false
|
||||
// Update owner references
|
||||
ownerRef := metav1.OwnerReference{
|
||||
APIVersion: manualScalar.APIVersion,
|
||||
Kind: manualScalar.Kind,
|
||||
Name: manualScalar.Name,
|
||||
UID: manualScalar.UID,
|
||||
Controller: &isController,
|
||||
BlockOwnerDeletion: &bod,
|
||||
}
|
||||
// prepare for openApi schema check
|
||||
schemaDoc, err := r.DiscoveryClient.OpenAPISchema()
|
||||
if err != nil {
|
||||
return ctrl.Result{},
|
||||
util.EndReconcileWithNegativeCondition(ctx, r, &manualScalar, condition.ReconcileError(errors.Wrap(err, errQueryOpenAPI)))
|
||||
}
|
||||
document, err := openapi.NewOpenAPIData(schemaDoc)
|
||||
if err != nil {
|
||||
return ctrl.Result{},
|
||||
util.EndReconcileWithNegativeCondition(ctx, r, &manualScalar, condition.ReconcileError(errors.Wrap(err, errQueryOpenAPI)))
|
||||
}
|
||||
for _, res := range resources {
|
||||
if locateReplicaField(document, res) {
|
||||
found = true
|
||||
resPatch := client.MergeFrom(res.DeepCopy())
|
||||
klog.InfoS("Get the resource the trait is going to modify",
|
||||
"resource name", res.GetName(), "UID", res.GetUID())
|
||||
cpmeta.AddOwnerReference(res, ownerRef)
|
||||
err := unstructured.SetNestedField(res.Object, int64(manualScalar.Spec.ReplicaCount), "spec", "replicas")
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Failed to patch a resource for scaling")
|
||||
return ctrl.Result{},
|
||||
util.EndReconcileWithNegativeCondition(ctx, r, &manualScalar, condition.ReconcileError(errors.Wrap(err, errPatchTobeScaledResource)))
|
||||
}
|
||||
// merge patch to scale the resource
|
||||
if err := r.Patch(ctx, res, resPatch, client.FieldOwner(manualScalar.GetUID())); err != nil {
|
||||
klog.ErrorS(err, "Failed to scale a resource")
|
||||
return ctrl.Result{},
|
||||
util.EndReconcileWithNegativeCondition(ctx, r, &manualScalar, condition.ReconcileError(errors.Wrap(err, errScaleResource)))
|
||||
}
|
||||
klog.InfoS("Successfully scaled a resource", "resource GVK", res.GroupVersionKind().String(),
|
||||
"res UID", res.GetUID(), "target replica", manualScalar.Spec.ReplicaCount)
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
klog.InfoS("Cannot locate any resource", "total resources", len(resources))
|
||||
return ctrl.Result{},
|
||||
util.EndReconcileWithNegativeCondition(ctx, r, &manualScalar, condition.ReconcileError(errors.New(errScaleResource)))
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// locateReplicaField call openapi RESTFUL end point to fetch the schema of a given resource and try to see
|
||||
// if it has a spec.replicas filed that is of type integer. We will apply duck typing to modify the fields there
|
||||
// assuming that the fields is used to control the number of instances of this resource
|
||||
// NOTE: This only works if the resource CRD has a structural schema, all `apiextensions.k8s.io/v1` CRDs do
|
||||
// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema
|
||||
func locateReplicaField(document openapi.Resources, res *unstructured.Unstructured) bool {
|
||||
// this is the most common path for replicas fields
|
||||
replicaFieldPath := []string{"spec", "replicas"}
|
||||
gv, err := schema.ParseGroupVersion(res.GetAPIVersion())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// we look up the resource schema definition by its GVK
|
||||
schema := document.LookupResource(schema.GroupVersionKind{
|
||||
Group: gv.Group,
|
||||
Version: gv.Version,
|
||||
Kind: res.GetKind(),
|
||||
})
|
||||
// we try to see if there is a spec.replicas fields in its definition
|
||||
field, err := explain.LookupSchemaForField(schema, replicaFieldPath)
|
||||
if err != nil || field == nil {
|
||||
return false
|
||||
}
|
||||
// we also verify that it is of type integer to further narrow down the candidates
|
||||
replicaField, ok := field.(*proto.Primitive)
|
||||
if !ok || replicaField.Type != "integer" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SetupWithManager to setup k8s controller.
|
||||
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
name := "oam/" + strings.ToLower(oamv1alpha2.ManualScalerTraitKind)
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
Named(name).
|
||||
For(&oamv1alpha2.ManualScalerTrait{}).
|
||||
Complete(r)
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
Copyright 2021 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package manualscalertrait
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestManualscalertrait(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Manualscalertrait Suite")
|
||||
}
|
||||
|
|
@ -42,169 +42,6 @@ import (
|
|||
|
||||
var _ = Describe("Apply TraitDefinition to store its schema to ConfigMap Test", func() {
|
||||
ctx := context.Background()
|
||||
var ns corev1.Namespace
|
||||
|
||||
Context("When the TraitDefinition is valid, but the namespace doesn't exist, should occur errors", func() {
|
||||
It("Apply TraitDefinition", func() {
|
||||
By("Apply TraitDefinition")
|
||||
var validTraitDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
namespace: ns-tr-def
|
||||
annotations:
|
||||
definition.oam.dev/description: "Configures replicas for your service."
|
||||
name: scaler1
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
//+usage=Replicas of the workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
`
|
||||
|
||||
var def v1beta1.TraitDefinition
|
||||
Expect(yaml.Unmarshal([]byte(validTraitDefinition), &def)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &def)).Should(Not(Succeed()))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the TraitDefinition is valid, should create a ConfigMap", func() {
|
||||
var traitDefinitionName = "scaler1"
|
||||
var namespace = "ns-tr-def-1"
|
||||
req := reconcile.Request{NamespacedName: client.ObjectKey{Name: traitDefinitionName, Namespace: namespace}}
|
||||
|
||||
It("Apply TraitDefinition", func() {
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
By("Create a namespace")
|
||||
Expect(k8sClient.Create(ctx, &ns)).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("Apply TraitDefinition")
|
||||
var validTraitDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
namespace: ns-tr-def-1
|
||||
annotations:
|
||||
definition.oam.dev/description: "Configures replicas for your service."
|
||||
name: scaler1
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
//+usage=Replicas of the workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
`
|
||||
|
||||
var def v1beta1.TraitDefinition
|
||||
Expect(yaml.Unmarshal([]byte(validTraitDefinition), &def)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &def)).Should(Succeed())
|
||||
testutil.ReconcileRetry(&r, req)
|
||||
|
||||
By("Check whether ConfigMap is created")
|
||||
var cm corev1.ConfigMap
|
||||
name := fmt.Sprintf("trait-%s%s", types.CapabilityConfigMapNamePrefix, traitDefinitionName)
|
||||
Eventually(func() bool {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, &cm)
|
||||
return err == nil
|
||||
}, 30*time.Second, time.Second).Should(BeTrue())
|
||||
Expect(cm.Data[types.OpenapiV3JSONSchema]).Should(Not(Equal("")))
|
||||
Expect(cm.Labels["definition.oam.dev/name"]).Should(Equal(traitDefinitionName))
|
||||
|
||||
By("Check whether ConfigMapRef refer to right")
|
||||
Eventually(func() string {
|
||||
_ = k8sClient.Get(ctx, client.ObjectKey{Namespace: def.Namespace, Name: def.Name}, &def)
|
||||
return def.Status.ConfigMapRef
|
||||
}, 30*time.Second, time.Second).Should(Equal(name))
|
||||
|
||||
By("Delete the trait")
|
||||
Expect(k8sClient.Delete(ctx, &def)).Should(Succeed())
|
||||
testutil.ReconcileRetry(&r, req)
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the TraitDefinition is invalid, should report issues", func() {
|
||||
var invalidTraitDefinitionName = "invalid-tr1"
|
||||
var namespace = "ns-tr-def2"
|
||||
BeforeEach(func() {
|
||||
ns = corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: namespace,
|
||||
},
|
||||
}
|
||||
By("Create a namespace")
|
||||
Expect(k8sClient.Create(ctx, &ns)).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{}))
|
||||
})
|
||||
|
||||
It("Applying invalid TraitDefinition", func() {
|
||||
By("Apply the TraitDefinition")
|
||||
var invalidTraitDefinition = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
namespace: ns-tr-def2
|
||||
annotations:
|
||||
definition.oam.dev/description: "Configures replicas for your service."
|
||||
name: invalid-tr1
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: 2
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var invalidDef v1beta1.TraitDefinition
|
||||
Expect(yaml.Unmarshal([]byte(invalidTraitDefinition), &invalidDef)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &invalidDef)).Should(Succeed())
|
||||
gotTraitDefinition := &v1beta1.TraitDefinition{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: invalidTraitDefinitionName, Namespace: namespace}, gotTraitDefinition)).Should(BeNil())
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the CUE Template in TraitDefinition import new added CRD", func() {
|
||||
var traitDefinitionName = "test-refresh"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"github.com/oam-dev/kubevela/pkg/controller/common"
|
||||
controller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/core/scopes/healthscope"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1alpha2/core/traits/manualscalertrait"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/standard.oam.dev/v1alpha1/rollout"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/utils"
|
||||
)
|
||||
|
|
@ -33,7 +32,6 @@ func Setup(mgr ctrl.Manager, disableCaps string, args controller.Args) error {
|
|||
switch disableCaps {
|
||||
case common.DisableNoneCaps:
|
||||
functions = []func(ctrl.Manager, controller.Args) error{
|
||||
manualscalertrait.Setup,
|
||||
healthscope.Setup,
|
||||
rollout.Setup,
|
||||
}
|
||||
|
|
@ -41,9 +39,6 @@ func Setup(mgr ctrl.Manager, disableCaps string, args controller.Args) error {
|
|||
default:
|
||||
disableCapsSet := utils.StoreInSet(disableCaps)
|
||||
|
||||
if !disableCapsSet.Contains(common.ManualScalerTraitControllerName) {
|
||||
functions = append(functions, manualscalertrait.Setup)
|
||||
}
|
||||
if !disableCapsSet.Contains(common.HealthScopeControllerName) {
|
||||
functions = append(functions, healthscope.Setup)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,56 +117,6 @@ spec:
|
|||
})
|
||||
})
|
||||
|
||||
Context("When the definition is TraitDefinition", func() {
|
||||
var traitDefinitionName = "scaler1"
|
||||
|
||||
It("Test CapabilityTraitDefinition", func() {
|
||||
By("Apply TraitDefinition")
|
||||
var validTraitDefinition = `
|
||||
apiVersion: core.oam.dev/v1alpha2
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
namespace: ns-cap
|
||||
annotations:
|
||||
definition.oam.dev/description: "Configures replicas for your service."
|
||||
name: scaler1
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
//+usage=Replicas of the workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
`
|
||||
|
||||
var traitDefinition v1beta1.TraitDefinition
|
||||
Expect(yaml.Unmarshal([]byte(validTraitDefinition), &traitDefinition)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, &traitDefinition)).Should(Succeed())
|
||||
|
||||
def := &CapabilityTraitDefinition{Name: traitDefinitionName, TraitDefinition: *traitDefinition.DeepCopy()}
|
||||
|
||||
By("Test GetOpenAPISchema")
|
||||
var expectedSchema = "{\"properties\":{\"replicas\":{\"default\":1,\"description\":\"Replicas of the workload\",\"title\":\"replicas\",\"type\":\"integer\"}},\"required\":[\"replicas\"],\"type\":\"object\"}"
|
||||
schema, err := def.GetOpenAPISchema(pd, traitDefinitionName)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(string(schema)).Should(Equal(expectedSchema))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the definition is CapabilityBaseDefinition", func() {
|
||||
|
||||
It("Test CapabilityTraitDefinition", func() {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ const LabelPodSpecable = "workload.oam.dev/podspecable"
|
|||
// allBuiltinCapabilities includes all builtin controllers
|
||||
// TODO(zzxwill) needs to automatically discovery all controllers
|
||||
var allBuiltinCapabilities = mapset.NewSet(
|
||||
common.ManualScalerTraitControllerName,
|
||||
common.RolloutControllerName,
|
||||
common.HealthScopeControllerName,
|
||||
common.EnvBindingControllerName,
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *2 | int
|
||||
}
|
||||
|
|
@ -276,99 +276,6 @@ func TestScopeRelatedUtils(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTraitHelper(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
namespace := "oamNS"
|
||||
traitDefinitionKind := "TraitDefinition"
|
||||
mockVerision := "core.oam.dev/v1alpha2"
|
||||
traitDefinitionName := "mocktraits.core.oam.dev"
|
||||
traitDefinitionRefName := "mocktraits.core.oam.dev"
|
||||
traitDefinitionWorkloadRefPath := "spec.workloadRef"
|
||||
|
||||
mockTraitDefinition := v1alpha2.TraitDefinition{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: traitDefinitionKind,
|
||||
APIVersion: mockVerision,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: traitDefinitionName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
Reference: common.DefinitionReference{
|
||||
Name: traitDefinitionRefName,
|
||||
},
|
||||
RevisionEnabled: false,
|
||||
WorkloadRefPath: traitDefinitionWorkloadRefPath,
|
||||
AppliesToWorkloads: nil,
|
||||
},
|
||||
}
|
||||
|
||||
traitName := "ms-trait"
|
||||
|
||||
mockTrait := v1alpha2.ManualScalerTrait{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: traitName,
|
||||
},
|
||||
Spec: v1alpha2.ManualScalerTraitSpec{
|
||||
ReplicaCount: 3,
|
||||
},
|
||||
}
|
||||
|
||||
unstructuredTrait, _ := util.Object2Unstructured(mockTrait)
|
||||
|
||||
getErr := fmt.Errorf("get error")
|
||||
type fields struct {
|
||||
getFunc test.ObjectFn
|
||||
}
|
||||
type want struct {
|
||||
td *v1alpha2.TraitDefinition
|
||||
err error
|
||||
}
|
||||
|
||||
cases := map[string]struct {
|
||||
fields fields
|
||||
want want
|
||||
}{
|
||||
"FetchTraitDefinition fail when getTraitDefinition fails": {
|
||||
fields: fields{
|
||||
getFunc: func(obj client.Object) error {
|
||||
return getErr
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
td: nil,
|
||||
err: getErr,
|
||||
},
|
||||
},
|
||||
|
||||
"FetchTraitDefinition Success": {
|
||||
fields: fields{
|
||||
getFunc: func(obj client.Object) error {
|
||||
o, _ := obj.(*v1alpha2.TraitDefinition)
|
||||
td := mockTraitDefinition
|
||||
*o = td
|
||||
return nil
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
td: &mockTraitDefinition,
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, tc := range cases {
|
||||
tclient := test.MockClient{
|
||||
MockGet: test.NewMockGetFn(nil, tc.fields.getFunc),
|
||||
}
|
||||
got, err := util.FetchTraitDefinition(ctx, &tclient, mock.NewMockDiscoveryMapper(), unstructuredTrait)
|
||||
t.Log(fmt.Sprint("Running test: ", name))
|
||||
assert.Equal(t, tc.want.err, err)
|
||||
assert.Equal(t, tc.want.td, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUtils(t *testing.T) {
|
||||
// Test common variables
|
||||
ctx := context.Background()
|
||||
|
|
@ -595,116 +502,6 @@ func TestConvertWorkloadGVK2Def(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGenTraitName(t *testing.T) {
|
||||
mts := v1alpha2.ManualScalerTrait{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "ns",
|
||||
Name: "sample-manualscaler-trait",
|
||||
},
|
||||
Spec: v1alpha2.ManualScalerTraitSpec{
|
||||
ReplicaCount: 3,
|
||||
},
|
||||
}
|
||||
trait := v1alpha2.ManualScalerTrait{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "extend.oam.dev/v1alpha2",
|
||||
Kind: "ManualScalerTrait",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "ns",
|
||||
Name: "sample-manualscaler-trait",
|
||||
},
|
||||
Spec: v1alpha2.ManualScalerTraitSpec{
|
||||
ReplicaCount: 3,
|
||||
},
|
||||
}
|
||||
traitTemplate := &v1alpha2.ComponentTrait{
|
||||
Trait: runtime.RawExtension{
|
||||
Object: &trait,
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
template *v1alpha2.ComponentTrait
|
||||
definitionName string
|
||||
exp string
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
template: &v1alpha2.ComponentTrait{},
|
||||
definitionName: "",
|
||||
exp: "simple-trait-67b8949f8d",
|
||||
},
|
||||
{
|
||||
name: "service",
|
||||
template: &v1alpha2.ComponentTrait{},
|
||||
definitionName: "dummy",
|
||||
exp: "service-trait-67b8949f8d",
|
||||
},
|
||||
{
|
||||
name: "simple",
|
||||
template: &v1alpha2.ComponentTrait{
|
||||
Trait: runtime.RawExtension{
|
||||
Object: &mts,
|
||||
},
|
||||
},
|
||||
definitionName: "",
|
||||
exp: "simple-trait-69dbc6b96",
|
||||
},
|
||||
{
|
||||
name: "simple-definition",
|
||||
template: traitTemplate,
|
||||
definitionName: "autoscale",
|
||||
exp: "simple-definition-autoscale-" + util.ComputeHash(traitTemplate),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
got := util.GenTraitName(test.name, test.template, test.definitionName)
|
||||
t.Log(fmt.Sprint("Running test: ", test.name))
|
||||
assert.Equal(t, test.exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeHash(t *testing.T) {
|
||||
mts := v1alpha2.ManualScalerTrait{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "ns",
|
||||
Name: "sample-manualscaler-trait",
|
||||
},
|
||||
Spec: v1alpha2.ManualScalerTraitSpec{
|
||||
ReplicaCount: 3,
|
||||
},
|
||||
}
|
||||
|
||||
test := []struct {
|
||||
name string
|
||||
template *v1alpha2.ComponentTrait
|
||||
exp string
|
||||
}{
|
||||
{
|
||||
name: "simple",
|
||||
template: &v1alpha2.ComponentTrait{},
|
||||
exp: "67b8949f8d",
|
||||
},
|
||||
{
|
||||
name: "simple",
|
||||
template: &v1alpha2.ComponentTrait{
|
||||
Trait: runtime.RawExtension{
|
||||
Object: &mts,
|
||||
},
|
||||
},
|
||||
exp: "69dbc6b96",
|
||||
},
|
||||
}
|
||||
for _, test := range test {
|
||||
got := util.ComputeHash(test.template)
|
||||
|
||||
t.Log(fmt.Sprint("Running test: ", got))
|
||||
assert.Equal(t, test.exp, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeepHashObject(t *testing.T) {
|
||||
successCases := []func() interface{}{
|
||||
func() interface{} { return 8675309 },
|
||||
|
|
|
|||
|
|
@ -127,7 +127,6 @@ var _ = BeforeSuite(func(done Done) {
|
|||
wDDefJson, _ := yaml.YAMLToJSON([]byte(cDDefYaml))
|
||||
Expect(json.Unmarshal(wDDefJson, wd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, wd)).Should(BeNil())
|
||||
|
||||
td := &v1beta1.TraitDefinition{}
|
||||
tDDefJson, _ := yaml.YAMLToJSON([]byte(tDDefYaml))
|
||||
Expect(json.Unmarshal(tDDefJson, td)).Should(BeNil())
|
||||
|
|
@ -196,32 +195,30 @@ spec:
|
|||
|
||||
cmd?: [...string]
|
||||
}`
|
||||
|
||||
tDDefYaml = `
|
||||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/internal/scaler.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
definition.oam.dev/description: Manually scale K8s pod for your workload which follows the pod spec in path 'spec.template'.
|
||||
name: scaler
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
extension:
|
||||
template: |-
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *1 | int
|
||||
}
|
||||
- statefulsets.apps
|
||||
podDisruptive: false
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
parameter: {
|
||||
// +usage=Specify the number of workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
// +patchStrategy=retainKeys
|
||||
patch: spec: replicas: parameter.replicas
|
||||
`
|
||||
)
|
||||
|
|
|
|||
|
|
@ -83,59 +83,6 @@ metadata: {
|
|||
r.Equal(act.Phase, "")
|
||||
}
|
||||
|
||||
func TestRenderComponent(t *testing.T) {
|
||||
r := require.New(t)
|
||||
p := &provider{
|
||||
render: func(comp common.ApplicationComponent, patcher *value.Value, _, _, _ string) (*unstructured.Unstructured, []*unstructured.Unstructured, error) {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
},
|
||||
}, []*unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "core.oam.dev/v1alpha2",
|
||||
"kind": "ManualScalerTrait",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"trait.oam.dev/resource": "scaler",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{"replicaCount": int64(10)},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
v, err := value.NewValue(`value: {}`, nil, "")
|
||||
r.NoError(err)
|
||||
err = p.RenderComponent(nil, nil, v, nil)
|
||||
r.NoError(err)
|
||||
s, err := v.String()
|
||||
r.NoError(err)
|
||||
r.Equal(s, `value: {}
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
}
|
||||
outputs: {
|
||||
scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
metadata: {
|
||||
labels: {
|
||||
"trait.oam.dev/resource": "scaler"
|
||||
}
|
||||
}
|
||||
spec: {
|
||||
replicaCount: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
`)
|
||||
}
|
||||
|
||||
func TestLoadComponent(t *testing.T) {
|
||||
r := require.New(t)
|
||||
p := &provider{
|
||||
|
|
|
|||
|
|
@ -212,23 +212,24 @@ var _ = Describe("test GetCapabilityByName", func() {
|
|||
Expect(k8sClient.Create(ctx, &cd4)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("create TraitDefinition")
|
||||
data, _ = os.ReadFile("testdata/manualscalars.yaml")
|
||||
yaml.Unmarshal(data, &td1)
|
||||
yaml.Unmarshal(data, &td2)
|
||||
data3, _ := os.ReadFile("testdata/svcTraitDef.yaml")
|
||||
yaml.Unmarshal(data3, &td3)
|
||||
td1.Namespace = ns
|
||||
td1.Name = trait1
|
||||
Expect(k8sClient.Create(ctx, &td1)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
td2.Namespace = defaultNS
|
||||
td2.Name = trait2
|
||||
Expect(k8sClient.Create(ctx, &td2)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
td3.DeepCopyInto(&td1)
|
||||
td3.DeepCopyInto(&td2)
|
||||
td3.Namespace = ns
|
||||
td3.Name = trait3
|
||||
Expect(k8sClient.Create(ctx, &td3)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
td1.Name = trait1
|
||||
td2.Name = trait2
|
||||
td1.Namespace = ns
|
||||
td1.Name = trait1
|
||||
td1.SetResourceVersion("")
|
||||
Expect(k8sClient.Create(ctx, &td1)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
td2.Namespace = defaultNS
|
||||
td2.Name = trait2
|
||||
td2.SetResourceVersion("")
|
||||
Expect(k8sClient.Create(ctx, &td2)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
})
|
||||
|
||||
It("get capability", func() {
|
||||
|
|
@ -287,12 +288,8 @@ var _ = Describe("test GetNamespacedCapabilitiesFromCluster", func() {
|
|||
defaultNS string
|
||||
cd1 corev1beta1.ComponentDefinition
|
||||
cd2 corev1beta1.ComponentDefinition
|
||||
td1 corev1beta1.TraitDefinition
|
||||
td2 corev1beta1.TraitDefinition
|
||||
component1 string
|
||||
component2 string
|
||||
trait1 string
|
||||
trait2 string
|
||||
)
|
||||
BeforeEach(func() {
|
||||
c = common.Args{}
|
||||
|
|
@ -302,8 +299,6 @@ var _ = Describe("test GetNamespacedCapabilitiesFromCluster", func() {
|
|||
defaultNS = types.DefaultKubeVelaNS
|
||||
component1 = "cd1"
|
||||
component2 = "cd2"
|
||||
trait1 = "td1"
|
||||
trait2 = "td2"
|
||||
|
||||
By("create namespace")
|
||||
Expect(k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}})).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
|
@ -321,30 +316,18 @@ var _ = Describe("test GetNamespacedCapabilitiesFromCluster", func() {
|
|||
cd2.Name = component2
|
||||
Expect(k8sClient.Create(ctx, &cd2)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("create TraitDefinition")
|
||||
data, _ = os.ReadFile("testdata/manualscalars.yaml")
|
||||
yaml.Unmarshal(data, &td1)
|
||||
yaml.Unmarshal(data, &td2)
|
||||
td1.Namespace = ns
|
||||
td1.Name = trait1
|
||||
Expect(k8sClient.Create(ctx, &td1)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
td2.Namespace = defaultNS
|
||||
td2.Name = trait2
|
||||
Expect(k8sClient.Create(ctx, &td2)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
})
|
||||
|
||||
It("get namespaced capabilities", func() {
|
||||
Context("found all capabilities", func() {
|
||||
capabilities, err := GetNamespacedCapabilitiesFromCluster(ctx, ns, c, nil)
|
||||
Expect(len(capabilities)).Should(Equal(4))
|
||||
Expect(len(capabilities)).Should(Equal(2))
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
Context("found two capabilities with a bad namespace", func() {
|
||||
capabilities, err := GetNamespacedCapabilitiesFromCluster(ctx, "a-bad-ns", c, nil)
|
||||
Expect(len(capabilities)).Should(Equal(2))
|
||||
Expect(len(capabilities)).Should(Equal(1))
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: scaler
|
||||
namespace: default
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
extension:
|
||||
template: |-
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *2 | int
|
||||
}
|
||||
|
|
@ -37,7 +37,6 @@ import (
|
|||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
|
|
@ -136,117 +135,6 @@ var _ = Describe("Test application cross namespace resource", func() {
|
|||
}, 20*time.Second, 500*time.Millisecond).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test GC for cluster-scoped trait", func() {
|
||||
By("Install cluster-scoped trait's TraitDefinition")
|
||||
clusterTraitDef := &v1beta1.TraitDefinition{}
|
||||
Expect(yaml.Unmarshal([]byte(fmt.Sprintf(clusterScopeTraitDefYAML, namespace)), clusterTraitDef)).Should(Succeed())
|
||||
Expect(k8sClient.Create(ctx, clusterTraitDef)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("Install namespace-scoped trait's TraitDefinition")
|
||||
crossNamespaceTraitDef := &v1beta1.TraitDefinition{}
|
||||
Expect(yaml.Unmarshal([]byte(fmt.Sprintf(crossNsTdYaml, namespace, crossNamespace)), crossNamespaceTraitDef)).Should(Succeed())
|
||||
Expect(k8sClient.Create(ctx, crossNamespaceTraitDef)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("Verify TraitDefinition are created successfully")
|
||||
Eventually(func() error {
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: "cluster-scope-trait", Namespace: namespace}, &v1beta1.TraitDefinition{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: "cross-scaler", Namespace: namespace}, &v1beta1.TraitDefinition{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, 20*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Create Application")
|
||||
var (
|
||||
appName = "cluster-scope-trait-app"
|
||||
app = new(v1beta1.Application)
|
||||
componentName = "cluster-scope-trait-comp"
|
||||
)
|
||||
app = &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: componentName,
|
||||
Type: "worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"image": "nginx:latest"}`)},
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "cluster-scope-trait",
|
||||
Properties: &runtime.RawExtension{Raw: []byte("{}")},
|
||||
},
|
||||
{
|
||||
Type: "cross-scaler",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"replicas": 1}`)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, app)
|
||||
}, 20*time.Second, 2*time.Second).Should(Succeed())
|
||||
|
||||
By("Verify the cluster-scoped trait is created")
|
||||
// sample cluster-scoped trait is PersistentVolume
|
||||
pv := &corev1.PersistentVolume{}
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: "pv-" + componentName, Namespace: namespace}, pv)
|
||||
}, 60*time.Second, time.Second).Should(Succeed())
|
||||
|
||||
By("Remove the cluster-scope trait from application ")
|
||||
app = &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: componentName,
|
||||
Type: "worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"image": "nginx:latest"}`)},
|
||||
Traits: []common.ApplicationTrait{
|
||||
// remove the cluster-scoped trait and keep the
|
||||
// cross-namespaced trait.
|
||||
// if remove both, the resouce tracker will be deleted,
|
||||
// we intends to test the gc of cluster-scoped trait but
|
||||
// not cascading deletion
|
||||
{
|
||||
Type: "cross-scaler",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"replicas": 1}`)},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Eventually(func() error {
|
||||
if err := k8sClient.Patch(ctx, app.DeepCopy(), client.Merge); err != nil {
|
||||
return err
|
||||
}
|
||||
updatedApp := &v1beta1.Application{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: appName, Namespace: namespace}, updatedApp); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(updatedApp.Spec.Components) > 0 && len(updatedApp.Spec.Components[0].Traits) == 1 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("the cluster-scope trait has not been removed from application")
|
||||
}, 30*time.Second, 2*time.Second).Should(Succeed())
|
||||
|
||||
By("Verify cluster-scoped trait is deleted")
|
||||
Eventually(func() error {
|
||||
RequestReconcileNow(ctx, app)
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: "pv-" + componentName, Namespace: namespace}, pv)
|
||||
}, 20*time.Second, 2*time.Second).Should(SatisfyAll(&util.NotFoundMatcher{}))
|
||||
})
|
||||
|
||||
It("Test application have cross-namespace workload", func() {
|
||||
// install component definition
|
||||
crossCdJson, _ := yaml.YAMLToJSON([]byte(fmt.Sprintf(crossCompDefYaml, namespace, crossNamespace)))
|
||||
|
|
@ -342,221 +230,6 @@ var _ = Describe("Test application cross namespace resource", func() {
|
|||
}, time.Second*5, time.Millisecond*300).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test update application by add a cross namespace trait resource ", func() {
|
||||
var (
|
||||
appName = "test-app-2"
|
||||
app = new(v1beta1.Application)
|
||||
componentName = "test-app-2-comp"
|
||||
)
|
||||
// install component definition
|
||||
normalCdJson, _ := yaml.YAMLToJSON([]byte(fmt.Sprintf(normalCompDefYaml, namespace)))
|
||||
ncd := new(v1beta1.ComponentDefinition)
|
||||
Expect(json.Unmarshal(normalCdJson, ncd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, ncd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
crossTdJson, err := yaml.YAMLToJSON([]byte(fmt.Sprintf(crossNsTdYaml, namespace, crossNamespace)))
|
||||
Expect(err).Should(BeNil())
|
||||
ctd := new(v1beta1.TraitDefinition)
|
||||
Expect(json.Unmarshal(crossTdJson, ctd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, ctd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
app = &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: componentName,
|
||||
Type: "normal-worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, app)
|
||||
}, 15*time.Second, 300*time.Microsecond).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{}))
|
||||
resourceTracker := new(v1beta1.ResourceTracker)
|
||||
By("application contain a normal workload, check application and workload status")
|
||||
Eventually(func() error {
|
||||
app := new(v1beta1.Application)
|
||||
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
|
||||
return fmt.Errorf("error to create application %v", err)
|
||||
}
|
||||
if app.Status.Phase != common.ApplicationRunning || app.Status.ObservedGeneration != app.Generation {
|
||||
return fmt.Errorf("application status not running")
|
||||
}
|
||||
depolys := new(appsv1.DeploymentList)
|
||||
opts := []client.ListOption{
|
||||
client.InNamespace(namespace),
|
||||
client.MatchingLabels{
|
||||
oam.LabelAppName: appName,
|
||||
},
|
||||
}
|
||||
err := k8sClient.List(ctx, depolys, opts...)
|
||||
if err != nil || len(depolys.Items) != 1 {
|
||||
return fmt.Errorf("error workload number %v", err)
|
||||
}
|
||||
if err = k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, time.Second*5, time.Millisecond*500).Should(BeNil())
|
||||
|
||||
Eventually(func() error {
|
||||
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app.Spec.Components[0].Traits = []common.ApplicationTrait{
|
||||
{
|
||||
Type: "cross-scaler",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"replicas": 1}`)},
|
||||
},
|
||||
}
|
||||
return k8sClient.Update(ctx, app)
|
||||
}, time.Second*5, time.Millisecond*300).Should(BeNil())
|
||||
|
||||
By("add a cross namespace trait, check resourceTracker and trait status")
|
||||
Eventually(func() error {
|
||||
app := new(v1beta1.Application)
|
||||
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
|
||||
return fmt.Errorf("error to get application %v", err)
|
||||
}
|
||||
if app.Status.Phase != common.ApplicationRunning {
|
||||
return fmt.Errorf("application status not running")
|
||||
}
|
||||
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resourceTracker not generated %v", err)
|
||||
}
|
||||
mts := new(v1alpha2.ManualScalerTraitList)
|
||||
opts := []client.ListOption{
|
||||
client.InNamespace(crossNamespace),
|
||||
client.MatchingLabels{
|
||||
oam.LabelAppName: appName,
|
||||
},
|
||||
}
|
||||
err = k8sClient.List(ctx, mts, opts...)
|
||||
if err != nil || len(mts.Items) != 1 {
|
||||
return fmt.Errorf("failed generate cross namespace trait")
|
||||
}
|
||||
if len(resourceTracker.Spec.ManagedResources) != 2 {
|
||||
return fmt.Errorf("expect track %d resources, but got %d", 2, len(resourceTracker.Spec.ManagedResources))
|
||||
}
|
||||
return nil
|
||||
}, time.Second*5, time.Millisecond*500).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test update application by delete a cross namespace trait resource", func() {
|
||||
var (
|
||||
appName = "test-app-3"
|
||||
app = new(v1beta1.Application)
|
||||
componentName = "test-app-3-comp"
|
||||
)
|
||||
By("install component definition")
|
||||
normalCdJson, _ := yaml.YAMLToJSON([]byte(fmt.Sprintf(normalCompDefYaml, namespace)))
|
||||
ncd := new(v1beta1.ComponentDefinition)
|
||||
Expect(json.Unmarshal(normalCdJson, ncd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, ncd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
crossTdJson, err := yaml.YAMLToJSON([]byte(fmt.Sprintf(crossNsTdYaml, namespace, crossNamespace)))
|
||||
Expect(err).Should(BeNil())
|
||||
ctd := new(v1beta1.TraitDefinition)
|
||||
Expect(json.Unmarshal(crossTdJson, ctd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, ctd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
app = &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: componentName,
|
||||
Type: "normal-worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "cross-scaler",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"replicas": 1}`)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, app)
|
||||
}, 15*time.Second, 300*time.Microsecond).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{}))
|
||||
resourceTracker := new(v1beta1.ResourceTracker)
|
||||
By("create application will create a cross ns trait, and resourceTracker. check those status")
|
||||
Eventually(func() error {
|
||||
app = new(v1beta1.Application)
|
||||
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
|
||||
return fmt.Errorf("error to get application %v", err)
|
||||
}
|
||||
if app.Status.Phase != common.ApplicationRunning || app.Status.ObservedGeneration != app.Generation {
|
||||
return fmt.Errorf("application status not running")
|
||||
}
|
||||
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error to get resourceTracker %v", err)
|
||||
}
|
||||
mts := new(v1alpha2.ManualScalerTraitList)
|
||||
opts := []client.ListOption{
|
||||
client.InNamespace(crossNamespace),
|
||||
client.MatchingLabels{
|
||||
oam.LabelAppName: appName,
|
||||
},
|
||||
}
|
||||
err = k8sClient.List(ctx, mts, opts...)
|
||||
if err != nil || len(mts.Items) != 1 {
|
||||
return fmt.Errorf("failed generate cross namespace trait")
|
||||
}
|
||||
if len(resourceTracker.Spec.ManagedResources) != 2 {
|
||||
return fmt.Errorf("expect track %q resources, but got %q", 2, len(resourceTracker.Spec.ManagedResources))
|
||||
}
|
||||
return nil
|
||||
}, time.Second*5, time.Millisecond*300).Should(BeNil())
|
||||
|
||||
By("update application trait by delete cross ns trait")
|
||||
Eventually(func() error {
|
||||
app = new(v1beta1.Application)
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
|
||||
app.Spec.Components[0].Traits = []common.ApplicationTrait{}
|
||||
return k8sClient.Update(ctx, app)
|
||||
}, time.Second*5, time.Millisecond*300).Should(BeNil())
|
||||
|
||||
Eventually(func() error {
|
||||
app = new(v1beta1.Application)
|
||||
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
|
||||
return fmt.Errorf("error to get application %v", err)
|
||||
}
|
||||
if app.Status.Phase != common.ApplicationRunning {
|
||||
return fmt.Errorf("application status not running")
|
||||
}
|
||||
if err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker); err != nil {
|
||||
return err
|
||||
}
|
||||
mts := new(v1alpha2.ManualScalerTraitList)
|
||||
opts := []client.ListOption{
|
||||
client.InNamespace(crossNamespace),
|
||||
client.MatchingLabels{
|
||||
oam.LabelAppName: appName,
|
||||
},
|
||||
}
|
||||
err = k8sClient.List(ctx, mts, opts...)
|
||||
if err != nil || len(mts.Items) != 0 {
|
||||
return fmt.Errorf("cross ns trait still exist")
|
||||
}
|
||||
return nil
|
||||
}, time.Second*5, time.Millisecond*500).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test application have two different workload", func() {
|
||||
var (
|
||||
appName = "test-app-4"
|
||||
|
|
@ -957,158 +630,6 @@ var _ = Describe("Test application cross namespace resource", func() {
|
|||
}, time.Second*5, time.Millisecond*500).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test cross-namespace resource gc logic, delete a cross-ns trait", func() {
|
||||
var (
|
||||
appName = "test-app-7"
|
||||
app = new(v1beta1.Application)
|
||||
componentName = "test-app-7-comp"
|
||||
)
|
||||
By("install related definition")
|
||||
|
||||
crossCdJson, err := yaml.YAMLToJSON([]byte(fmt.Sprintf(crossCompDefYaml, namespace, crossNamespace)))
|
||||
Expect(err).Should(BeNil())
|
||||
ctd := new(v1beta1.ComponentDefinition)
|
||||
Expect(json.Unmarshal(crossCdJson, ctd)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, ctd)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
crossTdJson, err := yaml.YAMLToJSON([]byte(fmt.Sprintf(crossNsTdYaml, namespace, crossNamespace)))
|
||||
Expect(err).Should(BeNil())
|
||||
td := new(v1beta1.TraitDefinition)
|
||||
Expect(json.Unmarshal(crossTdJson, td)).Should(BeNil())
|
||||
Expect(k8sClient.Create(ctx, td)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
app = &v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: componentName,
|
||||
Type: "cross-worker",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"cmd":["sleep","1000"],"image":"busybox"}`)},
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "cross-scaler",
|
||||
Properties: &runtime.RawExtension{Raw: []byte(`{"replicas": 0}`)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, app)
|
||||
}, 15*time.Second, 300*time.Microsecond).Should(SatisfyAny(Succeed(), &util.AlreadyExistMatcher{}))
|
||||
resourceTracker := new(v1beta1.ResourceTracker)
|
||||
By("create app and check resource and app status")
|
||||
Eventually(func() error {
|
||||
app = new(v1beta1.Application)
|
||||
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
|
||||
return fmt.Errorf("error to get application %v", err)
|
||||
}
|
||||
if app.Status.Phase != common.ApplicationRunning {
|
||||
return fmt.Errorf("application status not running")
|
||||
}
|
||||
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 1), resourceTracker)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error to get resourceTracker %v", err)
|
||||
}
|
||||
mts := new(v1alpha2.ManualScalerTraitList)
|
||||
opts := []client.ListOption{
|
||||
client.InNamespace(crossNamespace),
|
||||
client.MatchingLabels{
|
||||
oam.LabelAppName: appName,
|
||||
},
|
||||
}
|
||||
err = k8sClient.List(ctx, mts, opts...)
|
||||
if err != nil || len(mts.Items) != 1 {
|
||||
return fmt.Errorf("failed generate cross namespace trait")
|
||||
}
|
||||
if len(resourceTracker.Spec.ManagedResources) != 2 {
|
||||
return fmt.Errorf("expect track %q resources, but got %q", 2, len(resourceTracker.Spec.ManagedResources))
|
||||
}
|
||||
trait := mts.Items[0]
|
||||
deploys := new(appsv1.DeploymentList)
|
||||
err = k8sClient.List(ctx, deploys, opts...)
|
||||
if err != nil || len(deploys.Items) != 1 {
|
||||
return fmt.Errorf("error to list deploy")
|
||||
}
|
||||
deploy := deploys.Items[0]
|
||||
for _, resource := range resourceTracker.Spec.ManagedResources {
|
||||
if resource.Kind == deploy.Kind && resource.Name != deploy.Name {
|
||||
return fmt.Errorf("deploy name mismatch ")
|
||||
}
|
||||
if resource.Kind == trait.Kind && resource.Name != trait.Name {
|
||||
return fmt.Errorf("trait name mismatch")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, time.Second*5, time.Millisecond*500).Should(BeNil())
|
||||
|
||||
By("update application trait by delete cross ns trait, resourceTracker will still exist")
|
||||
Eventually(func() error {
|
||||
app = new(v1beta1.Application)
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
|
||||
app.Spec.Components[0].Traits = []common.ApplicationTrait{}
|
||||
return k8sClient.Update(ctx, app)
|
||||
}, time.Second*5, time.Millisecond*300).Should(BeNil())
|
||||
|
||||
Eventually(func() error {
|
||||
app = new(v1beta1.Application)
|
||||
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app); err != nil {
|
||||
return fmt.Errorf("error to get application %v", err)
|
||||
}
|
||||
if app.Status.Phase != common.ApplicationRunning {
|
||||
return fmt.Errorf("application status not running")
|
||||
}
|
||||
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error to get resourceTracker %v", err)
|
||||
}
|
||||
mts := new(v1alpha2.ManualScalerTraitList)
|
||||
opts := []client.ListOption{
|
||||
client.InNamespace(crossNamespace),
|
||||
client.MatchingLabels{
|
||||
oam.LabelAppName: appName,
|
||||
},
|
||||
}
|
||||
err = k8sClient.List(ctx, mts, opts...)
|
||||
if err != nil || len(mts.Items) != 0 {
|
||||
return fmt.Errorf("cross namespace trait still exist")
|
||||
}
|
||||
if len(resourceTracker.Spec.ManagedResources) != 1 {
|
||||
return fmt.Errorf("expect track %d resources, but got %d", 1, len(resourceTracker.Spec.ManagedResources))
|
||||
}
|
||||
deploys := new(appsv1.DeploymentList)
|
||||
err = k8sClient.List(ctx, deploys, opts...)
|
||||
if err != nil || len(deploys.Items) != 1 {
|
||||
return fmt.Errorf("error to list deploy")
|
||||
}
|
||||
deploy := deploys.Items[0]
|
||||
if resourceTracker.Spec.ManagedResources[0].Name != deploy.Name {
|
||||
return fmt.Errorf("error to record deploy name in app status")
|
||||
}
|
||||
return nil
|
||||
}, time.Second*30, time.Millisecond*500).Should(BeNil())
|
||||
By("deleting application will remove resourceTracker and related resourceTracker will be removed")
|
||||
app = new(v1beta1.Application)
|
||||
Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: appName}, app)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, app)).Should(BeNil())
|
||||
Eventually(func() error {
|
||||
err := k8sClient.Get(ctx, generateResourceTrackerKey(app.Namespace, app.Name, 2), resourceTracker)
|
||||
if err == nil {
|
||||
return fmt.Errorf("resourceTracker still exist")
|
||||
}
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, time.Second*5, time.Millisecond*500).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test cross-namespace resource gc logic, update a cross-ns workload's namespace", func() {
|
||||
// install related definition
|
||||
crossCdJson, _ := yaml.YAMLToJSON([]byte(fmt.Sprintf(crossCompDefYaml, namespace, crossNamespace)))
|
||||
|
|
@ -1337,37 +858,7 @@ spec:
|
|||
cmd?: [...string]
|
||||
}
|
||||
`
|
||||
crossNsTdYaml = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Manually scale the app"
|
||||
name: cross-scaler
|
||||
namespace: %s
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
workloadRefPath: spec.workloadRef
|
||||
extension:
|
||||
template: |-
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
metadata: {
|
||||
namespace: "%s"
|
||||
}
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *1 | int
|
||||
}
|
||||
`
|
||||
|
||||
clusterScopeTraitDefYAML = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package controllers_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
|
|
@ -31,17 +30,13 @@ import (
|
|||
. "github.com/onsi/gomega"
|
||||
|
||||
kruise "github.com/openkruise/kruise-api/apps/v1alpha1"
|
||||
"github.com/pkg/errors"
|
||||
networkv1 "k8s.io/api/networking/v1"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
k8sutils "k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
|
||||
|
|
@ -58,7 +53,6 @@ import (
|
|||
|
||||
var k8sClient client.Client
|
||||
var scheme = runtime.NewScheme()
|
||||
var manualscalertrait v1alpha2.TraitDefinition
|
||||
var roleName = "oam-example-com"
|
||||
var roleBindingName = "oam-role-binding"
|
||||
|
||||
|
|
@ -112,34 +106,6 @@ var _ = BeforeSuite(func(done Done) {
|
|||
}
|
||||
By("Finished setting up test environment")
|
||||
|
||||
detectAPIVersion()
|
||||
|
||||
// Create manual scaler trait definition
|
||||
manualscalertrait = v1alpha2.TraitDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "manualscalertraits.core.oam.dev",
|
||||
Namespace: "vela-system",
|
||||
Labels: map[string]string{"trait": "manualscalertrait"},
|
||||
},
|
||||
Spec: v1alpha2.TraitDefinitionSpec{
|
||||
WorkloadRefPath: "spec.workloadRef",
|
||||
Reference: commontypes.DefinitionReference{
|
||||
Name: "manualscalertraits.core.oam.dev",
|
||||
},
|
||||
},
|
||||
}
|
||||
// For some reason, traitDefinition is created as a Cluster scope object
|
||||
Expect(k8sClient.Create(context.Background(), manualscalertrait.DeepCopy())).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
Expect(k8sClient.Create(context.Background(), &manualscalertrait)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
// Create manual scaler trait definition with spec.extension field
|
||||
definitionExtension := DefinitionExtension{
|
||||
Alias: "ManualScaler",
|
||||
}
|
||||
in := new(runtime.RawExtension)
|
||||
in.Raw, _ = json.Marshal(definitionExtension)
|
||||
|
||||
By("Created extended manualscalertraits.core.oam.dev")
|
||||
|
||||
// create workload definition for 'deployments'
|
||||
wdDeploy := v1alpha2.WorkloadDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
|
@ -229,20 +195,3 @@ func RequestReconcileNow(ctx context.Context, o client.Object) {
|
|||
func randomNamespaceName(basic string) string {
|
||||
return fmt.Sprintf("%s-%s", basic, strconv.FormatInt(rand.Int63(), 16))
|
||||
}
|
||||
|
||||
// detectAPIVersion helps detect legacy GVK
|
||||
func detectAPIVersion() {
|
||||
err := k8sClient.Create(context.Background(), &networkv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-ingress",
|
||||
},
|
||||
Spec: networkv1.IngressSpec{
|
||||
IngressClassName: k8sutils.StringPtr("nginx"),
|
||||
Rules: []networkv1.IngressRule{},
|
||||
},
|
||||
})
|
||||
var noKindMatchErr = &meta.NoKindMatchError{}
|
||||
if err != nil && errors.As(err, &noKindMatchErr) {
|
||||
noNetworkingV1 = true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
"crd-manual-scaler": {
|
||||
type: "trait"
|
||||
annotations: {}
|
||||
labels: {}
|
||||
description: "Configures replicas for your service implemented by CRD controller."
|
||||
attributes: {
|
||||
podDisruptive: true
|
||||
appliesToWorkloads: ["deployments.apps"]
|
||||
workloadRefPath: "spec.workloadRef"
|
||||
definitionRef: name: "manualscalertraits.core.oam.dev"
|
||||
}
|
||||
}
|
||||
template: {
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: replicaCount: parameter.replicas
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
//+usage=Replicas of the workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
# Code generated by KubeVela templates. DO NOT EDIT. Please edit the original cue file.
|
||||
# Definition source cue file: vela-templates/definitions/registry/crd-manual-scaler.cue
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: Configures replicas for your service implemented by CRD controller.
|
||||
name: crd-manual-scaler
|
||||
namespace: vela-system
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- deployments.apps
|
||||
definitionRef:
|
||||
name: manualscalertraits.core.oam.dev
|
||||
podDisruptive: true
|
||||
schematic:
|
||||
cue:
|
||||
template: |
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: replicaCount: parameter.replicas
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
//+usage=Replicas of the workload
|
||||
replicas: *1 | int
|
||||
}
|
||||
workloadRefPath: spec.workloadRef
|
||||
|
||||
Loading…
Reference in New Issue