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:
Somefive 2022-09-14 16:28:09 +08:00 committed by GitHub
parent 1adc6d8803
commit ac52f4aba8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 117 additions and 4003 deletions

View File

@ -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"`
}

View File

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

View File

@ -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{})

View File

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

View File

@ -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: []

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

@ -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: []

View File

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

View File

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

View File

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

View File

@ -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: {}

View File

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

View File

@ -20,9 +20,6 @@ spec:
domain: "www.example.com"
http:
"/": 80
- type: myscaler
properties:
replicas: 2
- name: myweb-2
type: myworker
properties:

View File

@ -19,10 +19,7 @@ spec:
properties:
domain: "www.example.com"
http:
"/": 80
- type: myscaler
properties:
replicas: 2
"/": 80
- name: myweb-2
type: myworker
properties:

View File

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

View File

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

View File

@ -20,9 +20,6 @@ spec:
domain: "www.example.com"
http:
"/": 80
- type: myscaler
properties:
replicas: 2
- name: livediff-demo
type: ref-objects
properties:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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: {}

View File

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

View 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
}

View File

@ -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{}))
})
})

View File

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

View File

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

View File

@ -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")
}

View File

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

View File

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

View File

@ -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() {

View File

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

View File

@ -1,11 +0,0 @@
outputs: scaler: {
apiVersion: "core.oam.dev/v1alpha2"
kind: "ManualScalerTrait"
spec: {
replicaCount: parameter.replicas
}
}
parameter: {
//+short=r
replicas: *2 | int
}

View File

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

View File

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

View File

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

View File

@ -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())
})

View File

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

View File

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

View File

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

View File

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

View File

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