mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 10:00:06 +00:00
Chore: remove schematic kube and helm (#6099)
* Chore: remove unused code Signed-off-by: Somefive <yd219913@alibaba-inc.com> * Chore: remove schematic Kube & Helm Signed-off-by: Somefive <yd219913@alibaba-inc.com> --------- Signed-off-by: Somefive <yd219913@alibaba-inc.com>
This commit is contained in:
@@ -32,16 +32,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
// Kube defines the encapsulation in raw Kubernetes resource format
|
||||
type Kube struct {
|
||||
// Template defines the raw Kubernetes resource
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
Template runtime.RawExtension `json:"template"`
|
||||
|
||||
// Parameters defines configurable parameters
|
||||
Parameters []KubeParameter `json:"parameters,omitempty"`
|
||||
}
|
||||
|
||||
// ParameterValueType refers to a data type of parameter
|
||||
type ParameterValueType string
|
||||
|
||||
@@ -52,31 +42,6 @@ const (
|
||||
BooleanType ParameterValueType = "boolean"
|
||||
)
|
||||
|
||||
// A KubeParameter defines a configurable parameter of a component.
|
||||
type KubeParameter struct {
|
||||
// Name of this parameter
|
||||
Name string `json:"name"`
|
||||
|
||||
// +kubebuilder:validation:Enum:=string;number;boolean
|
||||
// ValueType indicates the type of the parameter value, and
|
||||
// only supports basic data types: string, number, boolean.
|
||||
ValueType ParameterValueType `json:"type"`
|
||||
|
||||
// FieldPaths specifies an array of fields within this workload that will be
|
||||
// overwritten by the value of this parameter. All fields must be of the
|
||||
// same type. Fields are specified as JSON field paths without a leading
|
||||
// dot, for example 'spec.replicas'.
|
||||
FieldPaths []string `json:"fieldPaths"`
|
||||
|
||||
// +kubebuilder:default:=false
|
||||
// Required specifies whether or not a value for this parameter must be
|
||||
// supplied when authoring an Application.
|
||||
Required *bool `json:"required,omitempty"`
|
||||
|
||||
// Description of this parameter.
|
||||
Description *string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// CUE defines the encapsulation in CUE format
|
||||
type CUE struct {
|
||||
// Template defines the abstraction template data of the capability, it will replace the old CUE template in extension field.
|
||||
@@ -87,26 +52,11 @@ type CUE struct {
|
||||
// Schematic defines the encapsulation of this capability(workload/trait/scope),
|
||||
// the encapsulation can be defined in different ways, e.g. CUE/HCL(terraform)/KUBE(K8s Object)/HELM, etc...
|
||||
type Schematic struct {
|
||||
KUBE *Kube `json:"kube,omitempty"`
|
||||
|
||||
CUE *CUE `json:"cue,omitempty"`
|
||||
|
||||
HELM *Helm `json:"helm,omitempty"`
|
||||
|
||||
Terraform *Terraform `json:"terraform,omitempty"`
|
||||
}
|
||||
|
||||
// A Helm represents resources used by a Helm module
|
||||
type Helm struct {
|
||||
// Release records a Helm release used by a Helm module workload.
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
Release runtime.RawExtension `json:"release"`
|
||||
|
||||
// HelmRelease records a Helm repository used by a Helm module workload.
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
Repository runtime.RawExtension `json:"repository"`
|
||||
}
|
||||
|
||||
// Terraform is the struct to describe cloud resources managed by Hashicorp Terraform
|
||||
type Terraform struct {
|
||||
// Configuration is Terraform Configuration
|
||||
@@ -213,26 +163,6 @@ const (
|
||||
ApplicationDeleting ApplicationPhase = "deleting"
|
||||
)
|
||||
|
||||
// WorkflowState is a string that mark the workflow state
|
||||
type WorkflowState string
|
||||
|
||||
const (
|
||||
// WorkflowStateInitializing means the workflow is in initial state
|
||||
WorkflowStateInitializing WorkflowState = "Initializing"
|
||||
// WorkflowStateTerminated means workflow is terminated manually, and it won't be started unless the spec changed.
|
||||
WorkflowStateTerminated WorkflowState = "Terminated"
|
||||
// WorkflowStateSuspended means workflow is suspended manually, and it can be resumed.
|
||||
WorkflowStateSuspended WorkflowState = "Suspended"
|
||||
// WorkflowStateSucceeded means workflow is running successfully, all steps finished.
|
||||
WorkflowStateSucceeded WorkflowState = "Succeeded"
|
||||
// WorkflowStateFinished means workflow is end.
|
||||
WorkflowStateFinished WorkflowState = "Finished"
|
||||
// WorkflowStateExecuting means workflow is still running or waiting some steps.
|
||||
WorkflowStateExecuting WorkflowState = "Executing"
|
||||
// WorkflowStateSkipping means it will skip this reconcile and let next reconcile to handle it.
|
||||
WorkflowStateSkipping WorkflowState = "Skipping"
|
||||
)
|
||||
|
||||
// ApplicationComponentStatus record the health status of App component
|
||||
type ApplicationComponentStatus struct {
|
||||
Name string `json:"name"`
|
||||
@@ -269,13 +199,6 @@ type Revision struct {
|
||||
RevisionHash string `json:"revisionHash,omitempty"`
|
||||
}
|
||||
|
||||
// RawComponent record raw component
|
||||
type RawComponent struct {
|
||||
// +kubebuilder:validation:EmbeddedResource
|
||||
// +kubebuilder:pruning:PreserveUnknownFields
|
||||
Raw runtime.RawExtension `json:"raw"`
|
||||
}
|
||||
|
||||
// AppStatus defines the observed state of Application
|
||||
type AppStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
|
||||
@@ -278,76 +278,6 @@ func (in *DefinitionReference) DeepCopy() *DefinitionReference {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Helm) DeepCopyInto(out *Helm) {
|
||||
*out = *in
|
||||
in.Release.DeepCopyInto(&out.Release)
|
||||
in.Repository.DeepCopyInto(&out.Repository)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Helm.
|
||||
func (in *Helm) DeepCopy() *Helm {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Helm)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Kube) DeepCopyInto(out *Kube) {
|
||||
*out = *in
|
||||
in.Template.DeepCopyInto(&out.Template)
|
||||
if in.Parameters != nil {
|
||||
in, out := &in.Parameters, &out.Parameters
|
||||
*out = make([]KubeParameter, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Kube.
|
||||
func (in *Kube) DeepCopy() *Kube {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Kube)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *KubeParameter) DeepCopyInto(out *KubeParameter) {
|
||||
*out = *in
|
||||
if in.FieldPaths != nil {
|
||||
in, out := &in.FieldPaths, &out.FieldPaths
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Required != nil {
|
||||
in, out := &in.Required, &out.Required
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.Description != nil {
|
||||
in, out := &in.Description, &out.Description
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeParameter.
|
||||
func (in *KubeParameter) DeepCopy() *KubeParameter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(KubeParameter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *OAMObjectReference) DeepCopyInto(out *OAMObjectReference) {
|
||||
*out = *in
|
||||
@@ -383,22 +313,6 @@ func (in *PolicyStatus) DeepCopy() *PolicyStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RawComponent) DeepCopyInto(out *RawComponent) {
|
||||
*out = *in
|
||||
in.Raw.DeepCopyInto(&out.Raw)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RawComponent.
|
||||
func (in *RawComponent) DeepCopy() *RawComponent {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(RawComponent)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *RawExtensionPointer) DeepCopyInto(out *RawExtensionPointer) {
|
||||
*out = *in
|
||||
@@ -475,21 +389,11 @@ func (in *Revision) DeepCopy() *Revision {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Schematic) DeepCopyInto(out *Schematic) {
|
||||
*out = *in
|
||||
if in.KUBE != nil {
|
||||
in, out := &in.KUBE, &out.KUBE
|
||||
*out = new(Kube)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.CUE != nil {
|
||||
in, out := &in.CUE, &out.CUE
|
||||
*out = new(CUE)
|
||||
**out = **in
|
||||
}
|
||||
if in.HELM != nil {
|
||||
in, out := &in.HELM, &out.HELM
|
||||
*out = new(Helm)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Terraform != nil {
|
||||
in, out := &in.Terraform, &out.Terraform
|
||||
*out = new(Terraform)
|
||||
|
||||
@@ -32,7 +32,6 @@ import (
|
||||
"github.com/kubevela/pkg/util/compression"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/interfaces"
|
||||
velatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
velaerr "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
@@ -53,8 +52,7 @@ type ResourceTracker struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec ResourceTrackerSpec `json:"spec,omitempty"`
|
||||
Status ResourceTrackerStatus `json:"status,omitempty"`
|
||||
Spec ResourceTrackerSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// ResourceTrackerType defines the type of resourceTracker
|
||||
@@ -140,7 +138,7 @@ type ManagedResource struct {
|
||||
}
|
||||
|
||||
// Equal check if two managed resource equals
|
||||
func (in ManagedResource) Equal(r ManagedResource) bool {
|
||||
func (in *ManagedResource) Equal(r ManagedResource) bool {
|
||||
if !in.ClusterObjectReference.Equal(r.ClusterObjectReference) {
|
||||
return false
|
||||
}
|
||||
@@ -151,7 +149,7 @@ func (in ManagedResource) Equal(r ManagedResource) bool {
|
||||
}
|
||||
|
||||
// DisplayName readable name for locating resource
|
||||
func (in ManagedResource) DisplayName() string {
|
||||
func (in *ManagedResource) DisplayName() string {
|
||||
s := in.Kind + " " + in.Name
|
||||
if in.Namespace != "" || in.Cluster != "" {
|
||||
s += " ("
|
||||
@@ -170,12 +168,12 @@ func (in ManagedResource) DisplayName() string {
|
||||
}
|
||||
|
||||
// NamespacedName namespacedName
|
||||
func (in ManagedResource) NamespacedName() types.NamespacedName {
|
||||
func (in *ManagedResource) NamespacedName() types.NamespacedName {
|
||||
return types.NamespacedName{Namespace: in.Namespace, Name: in.Name}
|
||||
}
|
||||
|
||||
// ResourceKey computes the key for managed resource, resources with the same key points to the same resource
|
||||
func (in ManagedResource) ResourceKey() string {
|
||||
func (in *ManagedResource) ResourceKey() string {
|
||||
group := in.GroupVersionKind().Group
|
||||
kind := in.GroupVersionKind().Kind
|
||||
cluster := in.Cluster
|
||||
@@ -186,12 +184,12 @@ func (in ManagedResource) ResourceKey() string {
|
||||
}
|
||||
|
||||
// ComponentKey computes the key for the component which managed resource belongs to
|
||||
func (in ManagedResource) ComponentKey() string {
|
||||
func (in *ManagedResource) ComponentKey() string {
|
||||
return strings.Join([]string{in.Env, in.Component}, "/")
|
||||
}
|
||||
|
||||
// UnmarshalTo unmarshal ManagedResource into target object
|
||||
func (in ManagedResource) UnmarshalTo(obj interface{}) error {
|
||||
func (in *ManagedResource) UnmarshalTo(obj interface{}) error {
|
||||
if in.Data == nil || in.Data.Raw == nil {
|
||||
return velaerr.ManagedResourceHasNoDataError{}
|
||||
}
|
||||
@@ -199,7 +197,7 @@ func (in ManagedResource) UnmarshalTo(obj interface{}) error {
|
||||
}
|
||||
|
||||
// ToUnstructured converts managed resource into unstructured
|
||||
func (in ManagedResource) ToUnstructured() *unstructured.Unstructured {
|
||||
func (in *ManagedResource) ToUnstructured() *unstructured.Unstructured {
|
||||
obj := &unstructured.Unstructured{}
|
||||
obj.SetGroupVersionKind(in.GroupVersionKind())
|
||||
obj.SetName(in.Name)
|
||||
@@ -211,7 +209,7 @@ func (in ManagedResource) ToUnstructured() *unstructured.Unstructured {
|
||||
}
|
||||
|
||||
// ToUnstructuredWithData converts managed resource into unstructured and unmarshal data
|
||||
func (in ManagedResource) ToUnstructuredWithData() (*unstructured.Unstructured, error) {
|
||||
func (in *ManagedResource) ToUnstructuredWithData() (*unstructured.Unstructured, error) {
|
||||
obj := in.ToUnstructured()
|
||||
if err := in.UnmarshalTo(obj); err != nil {
|
||||
if errors.Is(err, velaerr.ManagedResourceHasNoDataError{}) {
|
||||
@@ -221,13 +219,6 @@ func (in ManagedResource) ToUnstructuredWithData() (*unstructured.Unstructured,
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// ResourceTrackerStatus define the status of resourceTracker
|
||||
// For backward-compatibility
|
||||
type ResourceTrackerStatus struct {
|
||||
// Deprecated
|
||||
TrackedResources []common.ClusterObjectReference `json:"trackedResources,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
@@ -325,29 +316,3 @@ func (in *ResourceTracker) DeleteManagedResource(rsc client.Object, remove bool)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// addClusterObjectReference
|
||||
// Deprecated
|
||||
func (in *ResourceTracker) addClusterObjectReference(ref common.ClusterObjectReference) bool {
|
||||
for _, _rsc := range in.Status.TrackedResources {
|
||||
if _rsc.Equal(ref) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
in.Status.TrackedResources = append(in.Status.TrackedResources, ref)
|
||||
return false
|
||||
}
|
||||
|
||||
// AddTrackedResource add new object reference into tracked resources, return if already exists
|
||||
// Deprecated
|
||||
func (in *ResourceTracker) AddTrackedResource(rsc interfaces.TrackableResource) bool {
|
||||
return in.addClusterObjectReference(common.ClusterObjectReference{
|
||||
ObjectReference: corev1.ObjectReference{
|
||||
APIVersion: rsc.GetAPIVersion(),
|
||||
Kind: rsc.GetKind(),
|
||||
Name: rsc.GetName(),
|
||||
Namespace: rsc.GetNamespace(),
|
||||
UID: rsc.GetUID(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -679,7 +679,6 @@ func (in *ResourceTracker) DeepCopyInto(out *ResourceTracker) {
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceTracker.
|
||||
@@ -771,26 +770,6 @@ func (in *ResourceTrackerSpec) DeepCopy() *ResourceTrackerSpec {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourceTrackerStatus) DeepCopyInto(out *ResourceTrackerStatus) {
|
||||
*out = *in
|
||||
if in.TrackedResources != nil {
|
||||
in, out := &in.TrackedResources, &out.TrackedResources
|
||||
*out = make([]common.ClusterObjectReference, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceTrackerStatus.
|
||||
func (in *ResourceTrackerStatus) DeepCopy() *ResourceTrackerStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourceTrackerStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TraitDefinition) DeepCopyInto(out *TraitDefinition) {
|
||||
*out = *in
|
||||
|
||||
@@ -1,35 +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 interfaces
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// ObjectOwner is the interface for get and set ownerReference
|
||||
type ObjectOwner interface {
|
||||
GetOwnerReferences() []metav1.OwnerReference
|
||||
SetOwnerReferences([]metav1.OwnerReference)
|
||||
}
|
||||
|
||||
// TrackableResource is the interface for resources to be tracked by resourcetracker
|
||||
type TrackableResource interface {
|
||||
client.Object
|
||||
metav1.Type
|
||||
ObjectOwner
|
||||
}
|
||||
@@ -17,13 +17,7 @@ limitations under the License.
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
)
|
||||
|
||||
// Source record the source of Capability
|
||||
@@ -89,10 +83,6 @@ type CapabilityCategory string
|
||||
const (
|
||||
TerraformCategory CapabilityCategory = "terraform"
|
||||
|
||||
HelmCategory CapabilityCategory = "helm"
|
||||
|
||||
KubeCategory CapabilityCategory = "kube"
|
||||
|
||||
CUECategory CapabilityCategory = "cue"
|
||||
)
|
||||
|
||||
@@ -109,49 +99,6 @@ type Parameter struct {
|
||||
JSONType string `json:"jsonType,omitempty"`
|
||||
}
|
||||
|
||||
// SetFlagBy set cli flag from Parameter
|
||||
func SetFlagBy(flags *pflag.FlagSet, v Parameter) {
|
||||
name := v.Name
|
||||
if v.Alias != "" {
|
||||
name = v.Alias
|
||||
}
|
||||
// nolint:exhaustive
|
||||
switch v.Type {
|
||||
case cue.IntKind:
|
||||
var vv int64
|
||||
switch val := v.Default.(type) {
|
||||
case int64:
|
||||
vv = val
|
||||
case json.Number:
|
||||
vv, _ = val.Int64()
|
||||
case int:
|
||||
vv = int64(val)
|
||||
case float64:
|
||||
vv = int64(val)
|
||||
}
|
||||
flags.Int64P(name, v.Short, vv, v.Usage)
|
||||
case cue.StringKind:
|
||||
flags.StringP(name, v.Short, v.Default.(string), v.Usage)
|
||||
case cue.BoolKind:
|
||||
flags.BoolP(name, v.Short, v.Default.(bool), v.Usage)
|
||||
case cue.NumberKind, cue.FloatKind:
|
||||
var vv float64
|
||||
switch val := v.Default.(type) {
|
||||
case int64:
|
||||
vv = float64(val)
|
||||
case json.Number:
|
||||
vv, _ = val.Float64()
|
||||
case int:
|
||||
vv = float64(val)
|
||||
case float64:
|
||||
vv = val
|
||||
}
|
||||
flags.Float64P(name, v.Short, vv, v.Usage)
|
||||
default:
|
||||
// other types not supported yet
|
||||
}
|
||||
}
|
||||
|
||||
// Capability defines the content of a capability
|
||||
type Capability struct {
|
||||
Name string `json:"name"`
|
||||
@@ -181,8 +128,4 @@ type Capability struct {
|
||||
TerraformConfiguration string `json:"terraformConfiguration,omitempty"`
|
||||
ConfigurationType string `json:"configurationType,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
// KubeTemplate
|
||||
KubeTemplate runtime.RawExtension `json:"kubetemplate,omitempty"`
|
||||
KubeParameter []common.KubeParameter `json:"kubeparameter,omitempty"`
|
||||
}
|
||||
|
||||
@@ -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 types
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// WorkflowContext is the workflow context to pass into workflow objects.
|
||||
type WorkflowContext struct {
|
||||
AppName string `json:"appName,omitempty"`
|
||||
AppRevision string `json:"appRevision,omitempty"`
|
||||
WorkflowIndex int `json:"workflowIndex"`
|
||||
ResourceConfigMap corev1.LocalObjectReference `json:"resourceConfigMap,omitempty"`
|
||||
}
|
||||
@@ -968,79 +968,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm
|
||||
module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used
|
||||
by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository
|
||||
used by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable
|
||||
parameter of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array
|
||||
of fields within this workload that will
|
||||
be overwritten by the value of this parameter.
|
||||
\tAll fields must be of the same type. Fields
|
||||
are specified as JSON field paths without
|
||||
a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or
|
||||
not a value for this parameter must be supplied
|
||||
when authoring an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type
|
||||
of the parameter value, and only supports
|
||||
basic data types: string, number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes
|
||||
resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud
|
||||
resources managed by Hashicorp Terraform
|
||||
@@ -1353,79 +1280,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm
|
||||
module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used
|
||||
by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository
|
||||
used by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable
|
||||
parameter of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array
|
||||
of fields within this workload that will
|
||||
be overwritten by the value of this parameter.
|
||||
\tAll fields must be of the same type. Fields
|
||||
are specified as JSON field paths without
|
||||
a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or
|
||||
not a value for this parameter must be supplied
|
||||
when authoring an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type
|
||||
of the parameter value, and only supports
|
||||
basic data types: string, number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes
|
||||
resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud
|
||||
resources managed by Hashicorp Terraform
|
||||
@@ -1697,79 +1551,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm
|
||||
module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used
|
||||
by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository
|
||||
used by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable
|
||||
parameter of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array
|
||||
of fields within this workload that will
|
||||
be overwritten by the value of this parameter.
|
||||
\tAll fields must be of the same type. Fields
|
||||
are specified as JSON field paths without
|
||||
a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or
|
||||
not a value for this parameter must be supplied
|
||||
when authoring an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type
|
||||
of the parameter value, and only supports
|
||||
basic data types: string, number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes
|
||||
resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud
|
||||
resources managed by Hashicorp Terraform
|
||||
@@ -2193,79 +1974,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm
|
||||
module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used
|
||||
by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository
|
||||
used by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable
|
||||
parameter of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array
|
||||
of fields within this workload that will
|
||||
be overwritten by the value of this parameter.
|
||||
\tAll fields must be of the same type. Fields
|
||||
are specified as JSON field paths without
|
||||
a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or
|
||||
not a value for this parameter must be supplied
|
||||
when authoring an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type
|
||||
of the parameter value, and only supports
|
||||
basic data types: string, number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes
|
||||
resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud
|
||||
resources managed by Hashicorp Terraform
|
||||
@@ -2524,79 +2232,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm
|
||||
module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used
|
||||
by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository
|
||||
used by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable
|
||||
parameter of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array
|
||||
of fields within this workload that will
|
||||
be overwritten by the value of this parameter.
|
||||
\tAll fields must be of the same type. Fields
|
||||
are specified as JSON field paths without
|
||||
a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or
|
||||
not a value for this parameter must be supplied
|
||||
when authoring an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type
|
||||
of the parameter value, and only supports
|
||||
basic data types: string, number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes
|
||||
resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud
|
||||
resources managed by Hashicorp Terraform
|
||||
|
||||
@@ -101,76 +101,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used by a Helm
|
||||
module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository used by
|
||||
a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable parameter
|
||||
of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array of fields
|
||||
within this workload that will be overwritten by the
|
||||
value of this parameter. \tAll fields must be of the
|
||||
same type. Fields are specified as JSON field paths
|
||||
without a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or not a value
|
||||
for this parameter must be supplied when authoring
|
||||
an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type of the parameter
|
||||
value, and only supports basic data types: string,
|
||||
number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud resources
|
||||
managed by Hashicorp Terraform
|
||||
|
||||
@@ -142,78 +142,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm
|
||||
module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used by
|
||||
a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository
|
||||
used by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable
|
||||
parameter of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array
|
||||
of fields within this workload that will be
|
||||
overwritten by the value of this parameter.
|
||||
\tAll fields must be of the same type. Fields
|
||||
are specified as JSON field paths without
|
||||
a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or not
|
||||
a value for this parameter must be supplied
|
||||
when authoring an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type of
|
||||
the parameter value, and only supports basic
|
||||
data types: string, number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud
|
||||
resources managed by Hashicorp Terraform
|
||||
@@ -473,78 +401,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm
|
||||
module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used by
|
||||
a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository
|
||||
used by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable
|
||||
parameter of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array
|
||||
of fields within this workload that will be
|
||||
overwritten by the value of this parameter.
|
||||
\tAll fields must be of the same type. Fields
|
||||
are specified as JSON field paths without
|
||||
a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or not
|
||||
a value for this parameter must be supplied
|
||||
when authoring an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type of
|
||||
the parameter value, and only supports basic
|
||||
data types: string, number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud
|
||||
resources managed by Hashicorp Terraform
|
||||
@@ -805,78 +661,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm
|
||||
module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used by
|
||||
a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository
|
||||
used by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable
|
||||
parameter of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array
|
||||
of fields within this workload that will be
|
||||
overwritten by the value of this parameter.
|
||||
\tAll fields must be of the same type. Fields
|
||||
are specified as JSON field paths without
|
||||
a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or not
|
||||
a value for this parameter must be supplied
|
||||
when authoring an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type of
|
||||
the parameter value, and only supports basic
|
||||
data types: string, number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud
|
||||
resources managed by Hashicorp Terraform
|
||||
@@ -1112,78 +896,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm
|
||||
module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used by
|
||||
a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository
|
||||
used by a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable
|
||||
parameter of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array
|
||||
of fields within this workload that will be
|
||||
overwritten by the value of this parameter.
|
||||
\tAll fields must be of the same type. Fields
|
||||
are specified as JSON field paths without
|
||||
a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or not
|
||||
a value for this parameter must be supplied
|
||||
when authoring an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type of
|
||||
the parameter value, and only supports basic
|
||||
data types: string, number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud
|
||||
resources managed by Hashicorp Terraform
|
||||
|
||||
@@ -74,76 +74,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used by a Helm
|
||||
module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository used by
|
||||
a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable parameter
|
||||
of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array of fields
|
||||
within this workload that will be overwritten by the
|
||||
value of this parameter. \tAll fields must be of the
|
||||
same type. Fields are specified as JSON field paths
|
||||
without a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or not a value
|
||||
for this parameter must be supplied when authoring
|
||||
an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type of the parameter
|
||||
value, and only supports basic data types: string,
|
||||
number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud resources
|
||||
managed by Hashicorp Terraform
|
||||
|
||||
@@ -130,56 +130,6 @@ spec:
|
||||
required:
|
||||
- applicationGeneration
|
||||
type: object
|
||||
status:
|
||||
description: ResourceTrackerStatus define the status of resourceTracker
|
||||
For backward-compatibility
|
||||
properties:
|
||||
trackedResources:
|
||||
description: Deprecated
|
||||
items:
|
||||
description: ClusterObjectReference defines the object reference
|
||||
with cluster.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API version of the referent.
|
||||
type: string
|
||||
cluster:
|
||||
type: string
|
||||
creator:
|
||||
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
|
||||
x-kubernetes-map-type: atomic
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
||||
@@ -119,76 +119,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used by a Helm
|
||||
module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository used by
|
||||
a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable parameter
|
||||
of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array of fields
|
||||
within this workload that will be overwritten by the
|
||||
value of this parameter. \tAll fields must be of the
|
||||
same type. Fields are specified as JSON field paths
|
||||
without a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or not a value
|
||||
for this parameter must be supplied when authoring
|
||||
an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type of the parameter
|
||||
value, and only supports basic data types: string,
|
||||
number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud resources
|
||||
managed by Hashicorp Terraform
|
||||
|
||||
@@ -71,76 +71,6 @@ spec:
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
helm:
|
||||
description: A Helm represents resources used by a Helm module
|
||||
properties:
|
||||
release:
|
||||
description: Release records a Helm release used by a Helm
|
||||
module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
repository:
|
||||
description: HelmRelease records a Helm repository used by
|
||||
a Helm module workload.
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- release
|
||||
- repository
|
||||
type: object
|
||||
kube:
|
||||
description: Kube defines the encapsulation in raw Kubernetes
|
||||
resource format
|
||||
properties:
|
||||
parameters:
|
||||
description: Parameters defines configurable parameters
|
||||
items:
|
||||
description: A KubeParameter defines a configurable parameter
|
||||
of a component.
|
||||
properties:
|
||||
description:
|
||||
description: Description of this parameter.
|
||||
type: string
|
||||
fieldPaths:
|
||||
description: "FieldPaths specifies an array of fields
|
||||
within this workload that will be overwritten by the
|
||||
value of this parameter. \tAll fields must be of the
|
||||
same type. Fields are specified as JSON field paths
|
||||
without a leading dot, for example 'spec.replicas'."
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
name:
|
||||
description: Name of this parameter
|
||||
type: string
|
||||
required:
|
||||
default: false
|
||||
description: Required specifies whether or not a value
|
||||
for this parameter must be supplied when authoring
|
||||
an Application.
|
||||
type: boolean
|
||||
type:
|
||||
description: 'ValueType indicates the type of the parameter
|
||||
value, and only supports basic data types: string,
|
||||
number, boolean.'
|
||||
enum:
|
||||
- string
|
||||
- number
|
||||
- boolean
|
||||
type: string
|
||||
required:
|
||||
- fieldPaths
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
template:
|
||||
description: Template defines the raw Kubernetes resource
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
terraform:
|
||||
description: Terraform is the struct to describe cloud resources
|
||||
managed by Hashicorp Terraform
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/kubevela/pkg/controller/sharding"
|
||||
pkgmulticluster "github.com/kubevela/pkg/multicluster"
|
||||
utillog "github.com/kubevela/pkg/util/log"
|
||||
"github.com/kubevela/pkg/util/profiling"
|
||||
wfTypes "github.com/kubevela/workflow/pkg/types"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
@@ -53,7 +54,6 @@ type CoreOptions struct {
|
||||
InformerSyncPeriod time.Duration
|
||||
QPS float64
|
||||
Burst int
|
||||
PprofAddr string
|
||||
LeaderElectionResourceLock string
|
||||
LeaseDuration time.Duration
|
||||
RenewDeadLine time.Duration
|
||||
@@ -90,7 +90,6 @@ func NewCoreOptions() *CoreOptions {
|
||||
InformerSyncPeriod: 10 * time.Hour,
|
||||
QPS: 50,
|
||||
Burst: 100,
|
||||
PprofAddr: "",
|
||||
LeaderElectionResourceLock: "configmapsleases",
|
||||
LeaseDuration: 15 * time.Second,
|
||||
RenewDeadLine: 10 * time.Second,
|
||||
@@ -123,7 +122,6 @@ func (s *CoreOptions) Flags() cliflag.NamedFlagSets {
|
||||
"The re-sync period for informer in controller-runtime. This is a system-level configuration.")
|
||||
gfs.Float64Var(&s.QPS, "kube-api-qps", s.QPS, "the qps for reconcile clients. Low qps may lead to low throughput. High qps may give stress to api-server. Raise this value if concurrent-reconciles is set to be high.")
|
||||
gfs.IntVar(&s.Burst, "kube-api-burst", s.Burst, "the burst for reconcile clients. Recommend setting it qps*2.")
|
||||
gfs.StringVar(&s.PprofAddr, "pprof-addr", s.PprofAddr, "The address for pprof to use while exporting profiling results. The default value is empty which means do not expose it. Set it to address like :6666 to expose it.")
|
||||
gfs.StringVar(&s.LeaderElectionResourceLock, "leader-election-resource-lock", s.LeaderElectionResourceLock, "The resource lock to use for leader election")
|
||||
gfs.DurationVar(&s.LeaseDuration, "leader-election-lease-duration", s.LeaseDuration,
|
||||
"The duration that non-leader candidates will wait to force acquire leadership")
|
||||
@@ -163,6 +161,7 @@ func (s *CoreOptions) Flags() cliflag.NamedFlagSets {
|
||||
kfs := fss.FlagSet("klog")
|
||||
pkgclient.AddTimeoutControllerClientFlags(fss.FlagSet("controllerclient"))
|
||||
utillog.AddFlags(kfs)
|
||||
profiling.AddFlags(fss.FlagSet("profiling"))
|
||||
|
||||
if s.LogDebug {
|
||||
_ = kfs.Set("v", strconv.Itoa(int(commonconfig.LogDebug)))
|
||||
|
||||
@@ -60,7 +60,6 @@ func TestCoreOptions_Flags(t *testing.T) {
|
||||
"--max-workflow-wait-backoff-time=5",
|
||||
"--metrics-addr=/metrics",
|
||||
"--perf-enabled=true",
|
||||
"--pprof-addr=/debug/pprof",
|
||||
"--use-webhook=true",
|
||||
"--webhook-cert-dir=/path/to/cert",
|
||||
"--webhook-port=8080",
|
||||
@@ -86,7 +85,6 @@ func TestCoreOptions_Flags(t *testing.T) {
|
||||
InformerSyncPeriod: 3 * time.Second,
|
||||
QPS: 200,
|
||||
Burst: 500,
|
||||
PprofAddr: "/debug/pprof",
|
||||
LeaderElectionResourceLock: "/leases",
|
||||
LeaseDuration: 3 * time.Second,
|
||||
RenewDeadLine: 5 * time.Second,
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
velaclient "github.com/kubevela/pkg/controller/client"
|
||||
"github.com/kubevela/pkg/controller/sharding"
|
||||
"github.com/kubevela/pkg/meta"
|
||||
"github.com/kubevela/pkg/util/profiling"
|
||||
"github.com/kubevela/workflow/pkg/cue/packages"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -52,7 +53,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/monitor/watcher"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
pkgutil "github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
oamwebhook "github.com/oam-dev/kubevela/pkg/webhook/core.oam.dev"
|
||||
@@ -101,10 +101,7 @@ func run(ctx context.Context, s *options.CoreOptions) error {
|
||||
"QPS", restConfig.QPS,
|
||||
"Burst", restConfig.Burst,
|
||||
)
|
||||
|
||||
if s.PprofAddr != "" {
|
||||
go pkgutil.EnablePprof(s.PprofAddr, nil)
|
||||
}
|
||||
go profiling.StartProfilingServer(nil)
|
||||
|
||||
// wrapper the round tripper by multi cluster rewriter
|
||||
if s.EnableClusterGateway {
|
||||
|
||||
@@ -43,8 +43,6 @@ var app v1beta1.Application
|
||||
var testShowCdDef v1beta1.ComponentDefinition
|
||||
var testShowTdDef v1beta1.TraitDefinition
|
||||
var testCdDef v1beta1.ComponentDefinition
|
||||
var testCdDefWithHelm v1beta1.ComponentDefinition
|
||||
var testCdDefWithKube v1beta1.ComponentDefinition
|
||||
var testCdWithDeepCue v1beta1.ComponentDefinition
|
||||
var testTdDef v1beta1.TraitDefinition
|
||||
var testTdDefWithKube v1beta1.TraitDefinition
|
||||
@@ -80,14 +78,6 @@ var _ = BeforeSuite(func() {
|
||||
err = k8sClient.Create(ctx, &testCdDef)
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
Expect(yaml.Unmarshal([]byte(componentDefWithHelm), &testCdDefWithHelm)).Should(BeNil())
|
||||
err = k8sClient.Create(ctx, &testCdDefWithHelm)
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
Expect(yaml.Unmarshal([]byte(componentDefWithKube), &testCdDefWithKube)).Should(BeNil())
|
||||
err = k8sClient.Create(ctx, &testCdDefWithKube)
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
Expect(yaml.Unmarshal([]byte(componentWithDeepCue), &testCdWithDeepCue)).Should(BeNil())
|
||||
err = k8sClient.Create(ctx, &testCdWithDeepCue)
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
@@ -124,8 +114,6 @@ var _ = AfterSuite(func() {
|
||||
|
||||
Expect(k8sClient.Delete(ctx, &app)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, &testCdDef)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, &testCdDefWithHelm)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, &testCdDefWithKube)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, &testCdWithDeepCue)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, &testTdDef)).Should(BeNil())
|
||||
Expect(k8sClient.Delete(ctx, &testTdDefWithKube)).Should(BeNil())
|
||||
|
||||
@@ -131,29 +131,6 @@ var _ = Describe("Test Kubectl Plugin", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).Should(ContainSubstring(showTdResult))
|
||||
})
|
||||
It("Test show componentDefinition use Helm Charts as Workload", func() {
|
||||
Eventually(func() string {
|
||||
cdName := "test-webapp-chart"
|
||||
output, _ := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", cdName))
|
||||
return output
|
||||
}, 20*time.Second, time.Second).Should(ContainSubstring("Specification"))
|
||||
})
|
||||
It("Test show componentDefinition def with raw Kube mode", func() {
|
||||
cdName := "kube-worker"
|
||||
output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", cdName))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).Should(ContainSubstring("image"))
|
||||
Expect(output).Should(ContainSubstring("The value will be applied to fields: [spec.template.spec.containers[0].image]."))
|
||||
Expect(output).Should(ContainSubstring("port"))
|
||||
Expect(output).Should(ContainSubstring("the specific container port num which can accept external request."))
|
||||
})
|
||||
It("Test show traitDefinition def with raw Kube mode", func() {
|
||||
tdName := "service-kube"
|
||||
output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", tdName))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(output).Should(ContainSubstring("targetPort"))
|
||||
Expect(output).Should(ContainSubstring("target port num for service provider."))
|
||||
})
|
||||
It("Test show traitDefinition def with cue single map parameter", func() {
|
||||
tdName := "annotations"
|
||||
output, err := e2e.Exec(fmt.Sprintf("kubectl-vela show %s -n default", tdName))
|
||||
@@ -397,73 +374,6 @@ spec:
|
||||
|
||||
`
|
||||
|
||||
var componentDefWithHelm = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
name: test-webapp-chart
|
||||
namespace: default
|
||||
annotations:
|
||||
definition.oam.dev/description: helm chart for webapp
|
||||
spec:
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
schematic:
|
||||
helm:
|
||||
release:
|
||||
chart:
|
||||
spec:
|
||||
chart: "podinfo"
|
||||
version: "5.1.4"
|
||||
repository:
|
||||
url: "https://charts.kubevela.net/example/"
|
||||
`
|
||||
|
||||
var componentDefWithKube = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
name: kube-worker
|
||||
namespace: default
|
||||
spec:
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
schematic:
|
||||
kube:
|
||||
template:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
parameters:
|
||||
- name: image
|
||||
required: true
|
||||
type: string
|
||||
fieldPaths:
|
||||
- "spec.template.spec.containers[0].image"
|
||||
- name: port
|
||||
required: true
|
||||
type: string
|
||||
fieldPaths:
|
||||
- "spec.template.spec.containers[0].ports[0].containerPort"
|
||||
description: "the specific container port num which can accept external request."
|
||||
`
|
||||
|
||||
var traitDef = `
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
|
||||
@@ -20,14 +20,14 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/oam-dev/kubevela/e2e"
|
||||
"github.com/oam-dev/kubevela/references/apis"
|
||||
"github.com/oam-dev/kubevela/references/cli"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var (
|
||||
registryConfigs = []apis.RegistryConfig{
|
||||
registryConfigs = []cli.RegistryConfig{
|
||||
{
|
||||
Name: "e2e-oss-registry",
|
||||
URL: "oss://registry.e2e.net",
|
||||
|
||||
1
go.mod
1
go.mod
@@ -13,7 +13,6 @@ require (
|
||||
github.com/briandowns/spinner v1.23.0
|
||||
github.com/chartmuseum/helm-push v0.10.4
|
||||
github.com/containerd/containerd v1.7.1
|
||||
github.com/coreos/prometheus-operator v0.41.1
|
||||
github.com/crossplane/crossplane-runtime v0.19.2
|
||||
github.com/cue-exp/kubevelafix v0.0.0-20220922150317-aead819d979d
|
||||
github.com/dave/jennifer v1.6.1
|
||||
|
||||
@@ -23,9 +23,6 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"cuelang.org/go/cue/format"
|
||||
json2cue "cuelang.org/go/encoding/json"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
|
||||
"github.com/kubevela/pkg/util/slices"
|
||||
terraformapi "github.com/oam-dev/terraform-controller/api/v1beta2"
|
||||
@@ -35,7 +32,6 @@ import (
|
||||
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"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velaclient "github.com/kubevela/pkg/controller/client"
|
||||
@@ -47,7 +43,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/appfile/helm"
|
||||
"github.com/oam-dev/kubevela/pkg/auth"
|
||||
"github.com/oam-dev/kubevela/pkg/component"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/definition"
|
||||
@@ -317,10 +312,6 @@ func (af *Appfile) GenerateComponentManifest(wl *Workload, mutate func(*velaproc
|
||||
// generate context here to avoid nil pointer panic
|
||||
wl.Ctx = NewBasicContext(ctxData, wl.Params)
|
||||
switch wl.CapabilityCategory {
|
||||
case types.HelmCategory:
|
||||
return generateComponentFromHelmModule(wl, ctxData)
|
||||
case types.KubeCategory:
|
||||
return generateComponentFromKubeModule(wl, ctxData)
|
||||
case types.TerraformCategory:
|
||||
return generateComponentFromTerraformModule(wl, af.Name, af.Namespace)
|
||||
default:
|
||||
@@ -614,78 +605,6 @@ func evalWorkloadWithContext(pCtx process.Context, wl *Workload, ns, appName str
|
||||
return compManifest, nil
|
||||
}
|
||||
|
||||
// GenerateCUETemplate generate CUE Template from Kube module and Helm module
|
||||
func GenerateCUETemplate(wl *Workload) (string, error) {
|
||||
var templateStr string
|
||||
switch wl.CapabilityCategory {
|
||||
case types.KubeCategory:
|
||||
kubeObj := &unstructured.Unstructured{}
|
||||
|
||||
err := json.Unmarshal(wl.FullTemplate.Kube.Template.Raw, kubeObj)
|
||||
if err != nil {
|
||||
return templateStr, errors.Wrap(err, "cannot decode Kube template into K8s object")
|
||||
}
|
||||
|
||||
paramValues, err := resolveKubeParameters(wl.FullTemplate.Kube.Parameters, wl.Params)
|
||||
if err != nil {
|
||||
return templateStr, errors.WithMessage(err, "cannot resolve parameter settings")
|
||||
}
|
||||
if err := setParameterValuesToKubeObj(kubeObj, paramValues); err != nil {
|
||||
return templateStr, errors.WithMessage(err, "cannot set parameters value")
|
||||
}
|
||||
|
||||
// convert structured kube obj into CUE (go ==marshal==> json ==decoder==> cue)
|
||||
objRaw, err := kubeObj.MarshalJSON()
|
||||
if err != nil {
|
||||
return templateStr, errors.Wrap(err, "cannot marshal kube object")
|
||||
}
|
||||
cuectx := cuecontext.New()
|
||||
expr, err := json2cue.Extract("", objRaw)
|
||||
if err != nil {
|
||||
return templateStr, errors.Wrap(err, "cannot extract object into CUE")
|
||||
}
|
||||
v := cuectx.BuildExpr(expr)
|
||||
cueRaw, err := format.Node(v.Syntax())
|
||||
if err != nil {
|
||||
return templateStr, errors.Wrap(err, "cannot format CUE")
|
||||
}
|
||||
|
||||
// NOTE a hack way to enable using CUE capabilities on KUBE schematic workload
|
||||
templateStr = fmt.Sprintf(`
|
||||
output: %s`, string(cueRaw))
|
||||
case types.HelmCategory:
|
||||
gv, err := schema.ParseGroupVersion(wl.FullTemplate.Reference.Definition.APIVersion)
|
||||
if err != nil {
|
||||
return templateStr, err
|
||||
}
|
||||
targetWorkloadGVK := gv.WithKind(wl.FullTemplate.Reference.Definition.Kind)
|
||||
// NOTE this is a hack way to enable using CUE module capabilities on Helm module workload
|
||||
// construct an empty base workload according to its GVK
|
||||
templateStr = fmt.Sprintf(`
|
||||
output: {
|
||||
apiVersion: "%s"
|
||||
kind: "%s"
|
||||
}`, targetWorkloadGVK.GroupVersion().String(), targetWorkloadGVK.Kind)
|
||||
default:
|
||||
}
|
||||
return templateStr, nil
|
||||
}
|
||||
|
||||
func generateComponentFromKubeModule(wl *Workload, ctxData velaprocess.ContextData) (*types.ComponentManifest, error) {
|
||||
templateStr, err := GenerateCUETemplate(wl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wl.FullTemplate.TemplateStr = templateStr
|
||||
|
||||
// re-use the way CUE module generates comp & acComp
|
||||
compManifest, err := generateComponentFromCUEModule(wl, ctxData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return compManifest, nil
|
||||
}
|
||||
|
||||
func generateTerraformConfigurationWorkload(wl *Workload, ns string) (*unstructured.Unstructured, error) {
|
||||
if wl.FullTemplate == nil || wl.FullTemplate.Terraform == nil || wl.FullTemplate.Terraform.Configuration == "" {
|
||||
return nil, errors.New(errTerraformConfigurationIsNotSet)
|
||||
@@ -772,37 +691,6 @@ type paramValueSetting struct {
|
||||
FieldPaths []string
|
||||
}
|
||||
|
||||
func resolveKubeParameters(params []common.KubeParameter, settings map[string]interface{}) (paramValueSettings, error) {
|
||||
supported := map[string]*common.KubeParameter{}
|
||||
for _, p := range params {
|
||||
supported[p.Name] = p.DeepCopy()
|
||||
}
|
||||
|
||||
values := make(paramValueSettings)
|
||||
for name, v := range settings {
|
||||
// check unsupported parameter setting
|
||||
if supported[name] == nil {
|
||||
return nil, errors.Errorf("unsupported parameter %q", name)
|
||||
}
|
||||
// construct helper map
|
||||
values[name] = paramValueSetting{
|
||||
Value: v,
|
||||
ValueType: supported[name].ValueType,
|
||||
FieldPaths: supported[name].FieldPaths,
|
||||
}
|
||||
}
|
||||
|
||||
// check required parameter
|
||||
for _, p := range params {
|
||||
if p.Required != nil && *p.Required {
|
||||
if _, ok := values[p.Name]; !ok {
|
||||
return nil, errors.Errorf("require parameter %q", p.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func setParameterValuesToKubeObj(obj *unstructured.Unstructured, values paramValueSettings) error {
|
||||
paved := fieldpath.Pave(obj.Object)
|
||||
for paramName, v := range values {
|
||||
@@ -839,35 +727,6 @@ func setParameterValuesToKubeObj(obj *unstructured.Unstructured, values paramVal
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateComponentFromHelmModule(wl *Workload, ctxData velaprocess.ContextData) (*types.ComponentManifest, error) {
|
||||
templateStr, err := GenerateCUETemplate(wl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wl.FullTemplate.TemplateStr = templateStr
|
||||
|
||||
// re-use the way CUE module generates comp & acComp
|
||||
compManifest := &types.ComponentManifest{
|
||||
Name: wl.Name,
|
||||
Namespace: ctxData.Namespace,
|
||||
StandardWorkload: &unstructured.Unstructured{},
|
||||
}
|
||||
|
||||
if wl.FullTemplate.Reference.Type != types.AutoDetectWorkloadDefinition {
|
||||
compManifest, err = generateComponentFromCUEModule(wl, ctxData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
rls, repo, err := helm.RenderHelmReleaseAndHelmRepo(wl.FullTemplate.Helm, wl.Name, ctxData.AppName, ctxData.Namespace, wl.Params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
compManifest.PackagedWorkloadResources = []*unstructured.Unstructured{rls, repo}
|
||||
return compManifest, nil
|
||||
}
|
||||
|
||||
// GenerateContextDataFromAppFile generates process context data from app file
|
||||
func GenerateContextDataFromAppFile(appfile *Appfile, wlName string) velaprocess.ContextData {
|
||||
data := velaprocess.ContextData{
|
||||
|
||||
@@ -19,7 +19,6 @@ package appfile
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
@@ -35,7 +34,6 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/kubevela/workflow/pkg/cue/model"
|
||||
@@ -49,259 +47,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
var _ = Describe("Test Helm schematic appfile", func() {
|
||||
var (
|
||||
appName = "test-app"
|
||||
compName = "test-comp"
|
||||
workloadName = "test-workload"
|
||||
)
|
||||
|
||||
It("Test generate AppConfig resources from Helm schematic", func() {
|
||||
appFile := &Appfile{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
AppRevisionName: appName + "-v1",
|
||||
RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
|
||||
"scaler": {
|
||||
Spec: v1beta1.TraitDefinitionSpec{},
|
||||
},
|
||||
},
|
||||
Workloads: []*Workload{
|
||||
{
|
||||
Name: workloadName,
|
||||
Type: "webapp-chart",
|
||||
CapabilityCategory: oamtypes.HelmCategory,
|
||||
Params: map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "5.1.2",
|
||||
},
|
||||
},
|
||||
engine: definition.NewWorkloadAbstractEngine(compName, pd),
|
||||
Traits: []*Trait{
|
||||
{
|
||||
Name: "scaler",
|
||||
Params: map[string]interface{}{
|
||||
"replicas": float64(10),
|
||||
},
|
||||
engine: definition.NewTraitAbstractEngine("scaler", pd),
|
||||
},
|
||||
},
|
||||
FullTemplate: &Template{
|
||||
Reference: common.WorkloadTypeDescriptor{
|
||||
Definition: common.WorkloadGVK{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
Helm: &common.Helm{
|
||||
Release: *util.Object2RawExtension(map[string]interface{}{
|
||||
"chart": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"chart": "podinfo",
|
||||
"version": "5.1.4",
|
||||
},
|
||||
},
|
||||
}),
|
||||
Repository: *util.Object2RawExtension(map[string]interface{}{
|
||||
"url": "https://charts.kubevela.net/example/",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Generate ApplicationConfiguration and Components")
|
||||
components, err := appFile.GenerateComponentManifests()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
expectCompManifest := &oamtypes.ComponentManifest{
|
||||
Name: workloadName,
|
||||
StandardWorkload: &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"workload.oam.dev/type": "webapp-chart",
|
||||
"app.oam.dev/component": compName,
|
||||
"app.oam.dev/name": appName,
|
||||
"app.oam.dev/appRevision": appName + "-v1",
|
||||
}}}},
|
||||
PackagedWorkloadResources: []*unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "helm.toolkit.fluxcd.io/v2beta1",
|
||||
"kind": "HelmRelease",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": fmt.Sprintf("%s-%s", appName, compName),
|
||||
"namespace": "default",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"chart": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"sourceRef": map[string]interface{}{
|
||||
"kind": "HelmRepository",
|
||||
"name": fmt.Sprintf("%s-%s", appName, compName),
|
||||
"namespace": "default",
|
||||
},
|
||||
},
|
||||
},
|
||||
"interval": "5m0s",
|
||||
"values": map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "5.1.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "source.toolkit.fluxcd.io/v1beta1",
|
||||
"kind": "HelmRepository",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": fmt.Sprintf("%s-%s", appName, compName),
|
||||
"namespace": "default",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"url": "https://charts.kubevela.net/example/",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Verify expected ComponentManifest")
|
||||
diff := cmp.Diff(components[0], expectCompManifest)
|
||||
Expect(diff).ShouldNot(BeEmpty())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
var _ = Describe("Test Kube schematic appfile", func() {
|
||||
var (
|
||||
appName = "test-app"
|
||||
compName = "test-comp"
|
||||
)
|
||||
var testTemplate = func() runtime.RawExtension {
|
||||
yamlStr := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
ports:
|
||||
- containerPort: 80 `
|
||||
b, _ := yaml.YAMLToJSON([]byte(yamlStr))
|
||||
return runtime.RawExtension{Raw: b}
|
||||
}
|
||||
|
||||
var testAppfile = func() *Appfile {
|
||||
return &Appfile{
|
||||
AppRevisionName: appName + "-v1",
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
RelatedTraitDefinitions: map[string]*v1beta1.TraitDefinition{
|
||||
"scaler": {
|
||||
Spec: v1beta1.TraitDefinitionSpec{},
|
||||
},
|
||||
},
|
||||
Workloads: []*Workload{
|
||||
{
|
||||
Type: "kube-worker",
|
||||
CapabilityCategory: oamtypes.KubeCategory,
|
||||
Params: map[string]interface{}{
|
||||
"image": "nginx:1.14.0",
|
||||
},
|
||||
engine: definition.NewWorkloadAbstractEngine(compName, pd),
|
||||
Traits: []*Trait{
|
||||
{
|
||||
Name: "scaler",
|
||||
Params: map[string]interface{}{
|
||||
"replicas": float64(10),
|
||||
},
|
||||
engine: definition.NewTraitAbstractEngine("scaler", pd),
|
||||
},
|
||||
},
|
||||
FullTemplate: &Template{
|
||||
Kube: &common.Kube{
|
||||
Template: testTemplate(),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
Required: pointer.Bool(true),
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Reference: common.WorkloadTypeDescriptor{
|
||||
Definition: common.WorkloadGVK{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
It("Test generate AppConfig resources from Kube schematic", func() {
|
||||
By("Generate ApplicationConfiguration and Components")
|
||||
comps, err := testAppfile().GenerateComponentManifests()
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
expectWorkload := func() *unstructured.Unstructured {
|
||||
yamlStr := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.14.0
|
||||
ports:
|
||||
- containerPort: 80 `
|
||||
r := &unstructured.Unstructured{}
|
||||
_ = yaml.Unmarshal([]byte(yamlStr), r)
|
||||
return r
|
||||
}()
|
||||
|
||||
expectCompManifest := &oamtypes.ComponentManifest{
|
||||
Name: compName,
|
||||
StandardWorkload: expectWorkload,
|
||||
}
|
||||
By("Verify expected Component")
|
||||
diff := cmp.Diff(comps[0], expectCompManifest)
|
||||
Expect(diff).ShouldNot(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test missing set required parameter", func() {
|
||||
appfile := testAppfile()
|
||||
// remove parameter settings
|
||||
appfile.Workloads[0].Params = nil
|
||||
_, err := appfile.GenerateComponentManifests()
|
||||
|
||||
expectError := errors.WithMessage(errors.New(`require parameter "image"`), "cannot resolve parameter settings")
|
||||
diff := cmp.Diff(expectError, err, test.EquateErrors())
|
||||
Expect(diff).Should(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Test Workflow", func() {
|
||||
It("generate workflow task runners", func() {
|
||||
workflowStepDef := v1beta1.WorkflowStepDefinition{
|
||||
@@ -462,77 +207,6 @@ variable "password" {
|
||||
})
|
||||
})
|
||||
|
||||
func TestResolveKubeParameters(t *testing.T) {
|
||||
stringParam := &common.KubeParameter{
|
||||
Name: "strParam",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec"},
|
||||
}
|
||||
requiredParam := &common.KubeParameter{
|
||||
Name: "reqParam",
|
||||
Required: pointer.Bool(true),
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec"},
|
||||
}
|
||||
tests := map[string]struct {
|
||||
reason string
|
||||
params []common.KubeParameter
|
||||
settings map[string]interface{}
|
||||
want paramValueSettings
|
||||
wantErr error
|
||||
}{
|
||||
"EmptyParam": {
|
||||
reason: "Empty value settings and no error should be returned",
|
||||
want: make(paramValueSettings),
|
||||
},
|
||||
"UnsupportedParam": {
|
||||
reason: "An error shoulde be returned because of unsupported param",
|
||||
params: []common.KubeParameter{*stringParam},
|
||||
settings: map[string]interface{}{"unsupported": "invalid parameter"},
|
||||
want: nil,
|
||||
wantErr: errors.Errorf("unsupported parameter %q", "unsupported"),
|
||||
},
|
||||
"MissingRequiredParam": {
|
||||
reason: "An error should be returned because of missing required param",
|
||||
params: []common.KubeParameter{*stringParam, *requiredParam},
|
||||
settings: map[string]interface{}{"strParam": "string"},
|
||||
want: nil,
|
||||
wantErr: errors.Errorf("require parameter %q", "reqParam"),
|
||||
},
|
||||
"Succeed": {
|
||||
reason: "No error should be returned",
|
||||
params: []common.KubeParameter{*stringParam, *requiredParam},
|
||||
settings: map[string]interface{}{"strParam": "test", "reqParam": "test"},
|
||||
want: paramValueSettings{
|
||||
"strParam": paramValueSetting{
|
||||
Value: "test",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: stringParam.FieldPaths,
|
||||
},
|
||||
"reqParam": paramValueSetting{
|
||||
Value: "test",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: requiredParam.FieldPaths,
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for tcName, tc := range tests {
|
||||
t.Run(tcName, func(t *testing.T) {
|
||||
result, err := resolveKubeParameters(tc.params, tc.settings)
|
||||
if diff := cmp.Diff(tc.want, result); diff != "" {
|
||||
t.Fatalf("\nresolveKubeParameters(...)(...) -want +get \nreason:%s\n%s\n", tc.reason, diff)
|
||||
}
|
||||
if diff := cmp.Diff(tc.wantErr, err, test.EquateErrors()); diff != "" {
|
||||
t.Fatalf("\nresolveKubeParameters(...)(...) -want +get \nreason:%s\n%s\n", tc.reason, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestSetParameterValuesToKubeObj(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
reason string
|
||||
@@ -949,202 +623,6 @@ func TestGenerateTerraformConfigurationWorkload(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateCUETemplate(t *testing.T) {
|
||||
|
||||
var testCorrectTemplate = func() runtime.RawExtension {
|
||||
yamlStr := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
ports:
|
||||
- containerPort: 80 `
|
||||
b, _ := yaml.YAMLToJSON([]byte(yamlStr))
|
||||
return runtime.RawExtension{Raw: b}
|
||||
}
|
||||
|
||||
var testErrorTemplate = func() runtime.RawExtension {
|
||||
yamlStr := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
template:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
`
|
||||
b, _ := yaml.YAMLToJSON([]byte(yamlStr))
|
||||
return runtime.RawExtension{Raw: b}
|
||||
}
|
||||
|
||||
testcases := map[string]struct {
|
||||
workload *Workload
|
||||
expectData string
|
||||
hasError bool
|
||||
errInfo string
|
||||
}{"Kube workload with Correct template": {
|
||||
workload: &Workload{
|
||||
FullTemplate: &Template{
|
||||
Kube: &common.Kube{
|
||||
Template: testCorrectTemplate(),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
Required: pointer.Bool(true),
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Reference: common.WorkloadTypeDescriptor{
|
||||
Definition: common.WorkloadGVK{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
},
|
||||
Params: map[string]interface{}{
|
||||
"image": "nginx:1.14.0",
|
||||
},
|
||||
CapabilityCategory: oamtypes.KubeCategory,
|
||||
},
|
||||
expectData: `
|
||||
output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
spec: {
|
||||
selector: {
|
||||
matchLabels: {
|
||||
app: "nginx"
|
||||
}
|
||||
}
|
||||
template: {
|
||||
metadata: {
|
||||
labels: {
|
||||
app: "nginx"
|
||||
}
|
||||
}
|
||||
spec: {
|
||||
containers: [{
|
||||
image: "nginx:1.14.0"
|
||||
name: "nginx"
|
||||
}]
|
||||
ports: [{
|
||||
containerPort: 80
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}`,
|
||||
hasError: false,
|
||||
}, "Kube workload with wrong template": {
|
||||
workload: &Workload{
|
||||
FullTemplate: &Template{
|
||||
Kube: &common.Kube{
|
||||
Template: testErrorTemplate(),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
Required: pointer.Bool(true),
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Reference: common.WorkloadTypeDescriptor{
|
||||
Definition: common.WorkloadGVK{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
},
|
||||
Params: map[string]interface{}{
|
||||
"image": "nginx:1.14.0",
|
||||
},
|
||||
CapabilityCategory: oamtypes.KubeCategory,
|
||||
},
|
||||
hasError: true,
|
||||
errInfo: "cannot decode Kube template into K8s object: unexpected end of JSON input",
|
||||
}, "Kube workload with wrong parameter": {
|
||||
workload: &Workload{
|
||||
FullTemplate: &Template{
|
||||
Kube: &common.Kube{
|
||||
Template: testCorrectTemplate(),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
Required: pointer.Bool(true),
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Reference: common.WorkloadTypeDescriptor{
|
||||
Definition: common.WorkloadGVK{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
},
|
||||
Params: map[string]interface{}{
|
||||
"unsupported": "invalid parameter",
|
||||
},
|
||||
CapabilityCategory: oamtypes.KubeCategory,
|
||||
},
|
||||
hasError: true,
|
||||
errInfo: "cannot resolve parameter settings: unsupported parameter \"unsupported\"",
|
||||
}, "Helm workload with correct reference": {
|
||||
workload: &Workload{
|
||||
FullTemplate: &Template{
|
||||
Reference: common.WorkloadTypeDescriptor{
|
||||
Definition: common.WorkloadGVK{
|
||||
APIVersion: "app/v1",
|
||||
Kind: "deployment",
|
||||
},
|
||||
},
|
||||
},
|
||||
CapabilityCategory: oamtypes.HelmCategory,
|
||||
},
|
||||
hasError: false,
|
||||
expectData: `
|
||||
output: {
|
||||
apiVersion: "app/v1"
|
||||
kind: "deployment"
|
||||
}`,
|
||||
}, "Helm workload with wrong reference": {
|
||||
workload: &Workload{
|
||||
FullTemplate: &Template{
|
||||
Reference: common.WorkloadTypeDescriptor{
|
||||
Definition: common.WorkloadGVK{
|
||||
APIVersion: "app@//v1",
|
||||
Kind: "deployment",
|
||||
},
|
||||
},
|
||||
},
|
||||
CapabilityCategory: oamtypes.HelmCategory,
|
||||
},
|
||||
hasError: true,
|
||||
errInfo: "unexpected GroupVersion string: app@//v1",
|
||||
}}
|
||||
|
||||
for i, tc := range testcases {
|
||||
template, err := GenerateCUETemplate(tc.workload)
|
||||
assert.Equal(t, err != nil, tc.hasError)
|
||||
if tc.hasError {
|
||||
assert.Equal(t, tc.errInfo, err.Error())
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, tc.expectData, template, i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareArtifactsData(t *testing.T) {
|
||||
compManifests := []*oamtypes.ComponentManifest{
|
||||
{
|
||||
|
||||
@@ -1,111 +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 helm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
helmapi "github.com/oam-dev/kubevela/pkg/appfile/helm/flux2apis"
|
||||
commonutil "github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultIntervalDuration is the interval that flux controller reconcile HelmRelease and HelmRepository
|
||||
DefaultIntervalDuration = &metav1.Duration{Duration: 5 * time.Minute}
|
||||
)
|
||||
|
||||
// ConstructHelmReleaseName will format helm release name in a fixed way
|
||||
func ConstructHelmReleaseName(appName, compName string) string {
|
||||
return appName + "-" + compName
|
||||
}
|
||||
|
||||
// RenderHelmReleaseAndHelmRepo constructs HelmRelease and HelmRepository in unstructured format
|
||||
func RenderHelmReleaseAndHelmRepo(helmSpec *common.Helm, compName, appName, ns string, values map[string]interface{}) (*unstructured.Unstructured, *unstructured.Unstructured, error) {
|
||||
releaseSpec, repoSpec, err := decodeHelmSpec(helmSpec)
|
||||
if err != nil {
|
||||
return nil, nil, errors.WithMessage(err, "Helm spec is invalid")
|
||||
}
|
||||
if releaseSpec.Interval == nil {
|
||||
releaseSpec.Interval = DefaultIntervalDuration
|
||||
}
|
||||
if repoSpec.Interval == nil {
|
||||
repoSpec.Interval = DefaultIntervalDuration
|
||||
}
|
||||
|
||||
// construct unstructured HelmRepository object
|
||||
repoName := fmt.Sprintf("%s-%s", appName, compName)
|
||||
helmRepo := commonutil.GenerateUnstructuredObj(repoName, ns, helmapi.HelmRepositoryGVK)
|
||||
if err := commonutil.SetSpecObjIntoUnstructuredObj(repoSpec, helmRepo); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot set spec to HelmRepository")
|
||||
}
|
||||
|
||||
// construct unstructured HelmRelease object
|
||||
rlsName := ConstructHelmReleaseName(appName, compName)
|
||||
helmRelease := commonutil.GenerateUnstructuredObj(rlsName, ns, helmapi.HelmReleaseGVK)
|
||||
|
||||
// construct HelmRelease chart values
|
||||
chartValues := map[string]interface{}{}
|
||||
if releaseSpec.Values != nil {
|
||||
if err := json.Unmarshal(releaseSpec.Values.Raw, &chartValues); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot get chart values")
|
||||
}
|
||||
}
|
||||
for k, v := range values {
|
||||
// override values with settings from application
|
||||
chartValues[k] = v
|
||||
}
|
||||
if len(chartValues) > 0 {
|
||||
// avoid an empty map
|
||||
vJSON, err := json.Marshal(chartValues)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot get chart values")
|
||||
}
|
||||
releaseSpec.Values = &apiextensionsv1.JSON{Raw: vJSON}
|
||||
}
|
||||
|
||||
// reference HelmRepository by HelmRelease
|
||||
releaseSpec.Chart.Spec.SourceRef = helmapi.CrossNamespaceObjectReference{
|
||||
Kind: helmapi.HelmRepositoryKind,
|
||||
Namespace: ns,
|
||||
Name: repoName,
|
||||
}
|
||||
if err := commonutil.SetSpecObjIntoUnstructuredObj(releaseSpec, helmRelease); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot set spec to HelmRelease")
|
||||
}
|
||||
|
||||
return helmRelease, helmRepo, nil
|
||||
}
|
||||
|
||||
func decodeHelmSpec(h *common.Helm) (*helmapi.HelmReleaseSpec, *helmapi.HelmRepositorySpec, error) {
|
||||
releaseSpec := &helmapi.HelmReleaseSpec{}
|
||||
if err := json.Unmarshal(h.Release.Raw, releaseSpec); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Helm release spec is invalid")
|
||||
}
|
||||
repoSpec := &helmapi.HelmRepositorySpec{}
|
||||
if err := json.Unmarshal(h.Repository.Raw, repoSpec); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "Helm repository spec is invalid")
|
||||
}
|
||||
return releaseSpec, repoSpec, nil
|
||||
}
|
||||
@@ -1,101 +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 helm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
helmapi "github.com/oam-dev/kubevela/pkg/appfile/helm/flux2apis"
|
||||
)
|
||||
|
||||
func TestRenderHelmReleaseAndHelmRepo(t *testing.T) {
|
||||
h := testData("podinfo", "1.0.0", "test.com", "testSecret")
|
||||
chartValues := map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "1.0.1",
|
||||
},
|
||||
}
|
||||
rls, repo, err := RenderHelmReleaseAndHelmRepo(h, "test-comp", "test-app", "test-ns", chartValues)
|
||||
if err != nil {
|
||||
t.Fatalf("want: nil, got: %v", err)
|
||||
}
|
||||
|
||||
expectRls := &unstructured.Unstructured{}
|
||||
expectRls.SetGroupVersionKind(helmapi.HelmReleaseGVK)
|
||||
expectRls.SetName("test-app-test-comp")
|
||||
expectRls.SetNamespace("test-ns")
|
||||
unstructured.SetNestedMap(expectRls.Object, map[string]interface{}{
|
||||
"chart": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"chart": "podinfo",
|
||||
"version": "1.0.0",
|
||||
"sourceRef": map[string]interface{}{
|
||||
"kind": "HelmRepository",
|
||||
"name": "test-app-test-comp",
|
||||
"namespace": "test-ns",
|
||||
},
|
||||
},
|
||||
},
|
||||
"interval": "5m0s",
|
||||
"values": map[string]interface{}{"image": map[string]interface{}{"tag": "1.0.1"}},
|
||||
}, "spec")
|
||||
|
||||
if diff := cmp.Diff(expectRls, rls); diff != "" {
|
||||
t.Errorf("\n%s\nApply(...): -want , +got \n%s\n", "render HelmRelease", diff)
|
||||
}
|
||||
|
||||
expectRepo := &unstructured.Unstructured{}
|
||||
expectRepo.SetGroupVersionKind(helmapi.HelmRepositoryGVK)
|
||||
expectRepo.SetName("test-app-test-comp")
|
||||
expectRepo.SetNamespace("test-ns")
|
||||
unstructured.SetNestedMap(expectRepo.Object, map[string]interface{}{
|
||||
"url": "test.com",
|
||||
"secretRef": map[string]interface{}{
|
||||
"name": "testSecret",
|
||||
},
|
||||
"interval": "5m0s",
|
||||
}, "spec")
|
||||
|
||||
if diff := cmp.Diff(expectRepo, repo); diff != "" {
|
||||
t.Errorf("\n%s\nApply(...): -want , +got \n%s\n", "render HelmRepository", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func testData(chart, version, repoURL, secretName string) *common.Helm {
|
||||
rlsStr := fmt.Sprintf(
|
||||
`chart:
|
||||
spec:
|
||||
chart: "%s"
|
||||
version: "%s"`, chart, version)
|
||||
repoStr := fmt.Sprintf(
|
||||
`url: "%s"
|
||||
secretRef:
|
||||
name: "%s"`, repoURL, secretName)
|
||||
rlsJson, _ := yaml.YAMLToJSON([]byte(rlsStr))
|
||||
repoJson, _ := yaml.YAMLToJSON([]byte(repoStr))
|
||||
|
||||
h := &common.Helm{}
|
||||
h.Release.Raw = rlsJson
|
||||
h.Repository.Raw = repoJson
|
||||
return h
|
||||
}
|
||||
@@ -1,218 +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 helm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"cuelang.org/go/cue/format"
|
||||
"cuelang.org/go/encoding/openapi"
|
||||
"cuelang.org/go/encoding/yaml"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/pkg/errors"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
)
|
||||
|
||||
var (
|
||||
getters = getter.Providers{
|
||||
getter.Provider{
|
||||
Schemes: []string{"http", "https"},
|
||||
New: getter.NewHTTPGetter,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// GetChartValuesJSONSchema fetched the Chart bundle and get JSON schema of Values
|
||||
// file. If the Chart provides a 'values.json.schema' file, use it directly.
|
||||
// Otherwise, try to generate a JSON schema based on the Values file.
|
||||
func GetChartValuesJSONSchema(ctx context.Context, h *common.Helm) ([]byte, error) {
|
||||
releaseSpec, repoSpec, err := decodeHelmSpec(h)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "Helm spec is invalid")
|
||||
}
|
||||
chartSpec := releaseSpec.Chart.Spec
|
||||
files, err := loadChartFiles(ctx, repoSpec.URL, chartSpec.Chart, chartSpec.Version)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "cannot load Chart files")
|
||||
}
|
||||
var values *loader.BufferedFile
|
||||
for _, f := range files {
|
||||
switch f.Name {
|
||||
case "values.yaml", "values.yml":
|
||||
values = f
|
||||
case "values.schema.json":
|
||||
// use the JSON schema file if exists
|
||||
return f.Data, nil
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
if values == nil {
|
||||
return nil, errors.New("cannot find 'values.schema.json' or 'values.yaml' file in the Chart")
|
||||
}
|
||||
// try to generate a schema based on Values file
|
||||
generatedSchema, err := generateSchemaFromValues(values.Data)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "cannot generate schema from Values file")
|
||||
}
|
||||
return generatedSchema, nil
|
||||
}
|
||||
|
||||
// generateSchemaFromValues generate OpenAPIv3 schema based on Chart Values
|
||||
// file.
|
||||
func generateSchemaFromValues(values []byte) ([]byte, error) {
|
||||
valuesIdentifier := "values"
|
||||
cuectx := cuecontext.New()
|
||||
// convert Values yaml to CUE
|
||||
file, err := yaml.Extract("", string(values))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot extract Values.yaml to CUE")
|
||||
}
|
||||
ins := cuectx.BuildFile(file)
|
||||
// get the streamed CUE including the comments which will be used as
|
||||
// 'description' in the schema
|
||||
c, err := format.Node(ins.Value().Syntax(cue.Docs(true)), format.Simplify())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot format CUE generated from Values.yaml")
|
||||
}
|
||||
// cue openapi encoder only works on top-level identifier, we have to add
|
||||
// an identifier manually
|
||||
valuesStr := fmt.Sprintf("#%s:{\n%s\n}", valuesIdentifier, string(c))
|
||||
|
||||
val := cuecontext.New().CompileString(valuesStr)
|
||||
if val.Err() != nil {
|
||||
return nil, errors.Wrap(val.Err(), "cannot compile CUE generated from Values.yaml")
|
||||
}
|
||||
// generate OpenAPIv3 schema through cue openapi encoder
|
||||
rawSchema, err := openapi.Gen(val, &openapi.Config{})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot generate OpenAPIv3 schema")
|
||||
}
|
||||
rawSchema, err = makeSwaggerCompatible(rawSchema)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "cannot make CUE-generated schema compatible with Swagger")
|
||||
}
|
||||
|
||||
var out = &bytes.Buffer{}
|
||||
_ = json.Indent(out, rawSchema, "", " ")
|
||||
// load schema into Swagger to validate it compatible with Swagger OpenAPIv3
|
||||
fullSchemaBySwagger, err := openapi3.NewLoader().LoadFromData(out.Bytes())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot load schema by SwaggerLoader")
|
||||
}
|
||||
valuesSchema := fullSchemaBySwagger.Components.Schemas[valuesIdentifier].Value
|
||||
changeEnumToDefault(valuesSchema)
|
||||
|
||||
b, err := valuesSchema.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot marshall Values schema")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func loadChartFiles(ctx context.Context, repoURL, chart, version string) ([]*loader.BufferedFile, error) {
|
||||
url, err := repo.FindChartInRepoURL(repoURL, chart, version, "", "", "", getters)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot find Chart URL")
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "cannot fetch Chart from remote URL:%s", url)
|
||||
}
|
||||
//nolint:errcheck
|
||||
defer resp.Body.Close()
|
||||
files, err := loader.LoadArchiveFiles(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot load Chart files")
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// cue openapi encoder converts default in Chart Values as enum in schema
|
||||
// changing enum to default makes the schema consistent with Chart Values
|
||||
func changeEnumToDefault(schema *openapi3.Schema) {
|
||||
t := schema.Type
|
||||
switch t {
|
||||
case "object":
|
||||
for _, v := range schema.Properties {
|
||||
s := v.Value
|
||||
changeEnumToDefault(s)
|
||||
}
|
||||
case "array":
|
||||
if schema.Items != nil {
|
||||
changeEnumToDefault(schema.Items.Value)
|
||||
}
|
||||
}
|
||||
// change enum to default
|
||||
if len(schema.Enum) > 0 {
|
||||
schema.Default = schema.Enum[0]
|
||||
schema.Enum = nil
|
||||
}
|
||||
// remove all required fields, because fields in Values.yml are all optional
|
||||
schema.Required = nil
|
||||
}
|
||||
|
||||
// cue openapi encoder converts 'items' field in an array type field into array,
|
||||
// that's not compatible with OpenAPIv3. 'items' field should be an object.
|
||||
func makeSwaggerCompatible(d []byte) ([]byte, error) {
|
||||
m := map[string]interface{}{}
|
||||
err := json.Unmarshal(d, &m)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot unmarshall schema")
|
||||
}
|
||||
handleItemsOfArrayType(m)
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot marshall schema")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// handleItemsOfArrayType will convert all 'items' of array type from array to object
|
||||
// and remove enum in the items
|
||||
func handleItemsOfArrayType(t map[string]interface{}) {
|
||||
for _, v := range t {
|
||||
if next, ok := v.(map[string]interface{}); ok {
|
||||
handleItemsOfArrayType(next)
|
||||
}
|
||||
}
|
||||
if t["type"] == "array" {
|
||||
if i, ok := t["items"].([]interface{}); ok {
|
||||
if len(i) > 0 {
|
||||
if itemSpec, ok := i[0].(map[string]interface{}); ok {
|
||||
handleItemsOfArrayType(itemSpec)
|
||||
itemSpec["enum"] = nil
|
||||
t["items"] = itemSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,295 +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 helm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestGenerateSchemaFromValues(t *testing.T) {
|
||||
testValues, err := os.ReadFile("./testdata/values.yaml")
|
||||
if err != nil {
|
||||
t.Error(err, "cannot load test data")
|
||||
}
|
||||
wantSchema, err := os.ReadFile("./testdata/values.schema.json")
|
||||
if err != nil {
|
||||
t.Error(err, "cannot load expected data")
|
||||
}
|
||||
wantSchemaMap := map[string]interface{}{}
|
||||
// convert bytes to map for diff converience
|
||||
_ = json.Unmarshal(wantSchema, &wantSchemaMap)
|
||||
result, err := generateSchemaFromValues(testValues)
|
||||
if err != nil {
|
||||
t.Error(err, "failed generate schema from values")
|
||||
}
|
||||
resultMap := map[string]interface{}{}
|
||||
if err := json.Unmarshal(result, &resultMap); err != nil {
|
||||
t.Error(err, "cannot unmarshal result bytes")
|
||||
}
|
||||
if diff := cmp.Diff(resultMap, wantSchemaMap); diff != "" {
|
||||
t.Fatalf("\ngenerateSchemaFromValues(...)(...) -want +get \n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChartValuesJSONSchema(t *testing.T) {
|
||||
testHelm := testData("podinfo", "5.1.4", "https://charts.kubevela.net/example", "testSecret")
|
||||
wantSchema, err := os.ReadFile("./testdata/podinfo.values.schema.json")
|
||||
if err != nil {
|
||||
t.Error(err, "cannot load expected data")
|
||||
}
|
||||
wantSchemaMap := map[string]interface{}{}
|
||||
// convert bytes to map for diff converience
|
||||
_ = json.Unmarshal(wantSchema, &wantSchemaMap)
|
||||
result, err := GetChartValuesJSONSchema(context.Background(), testHelm)
|
||||
if err != nil {
|
||||
t.Error(err, "failed get schema")
|
||||
}
|
||||
resultMap := map[string]interface{}{}
|
||||
if err := json.Unmarshal(result, &resultMap); err != nil {
|
||||
t.Error(err, "cannot unmarshal result bytes")
|
||||
}
|
||||
if diff := cmp.Diff(resultMap, wantSchemaMap); diff != "" {
|
||||
t.Fatalf("\nGetChartValuesJSONSchema(...)(...) -want +get \n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangeEnumToDefault(t *testing.T) {
|
||||
// testData contains object, string, integer, bool, and array type fields
|
||||
// with enum and required values
|
||||
testData := `{"properties":{"array":{"enum":[["a","b","c"]],"items":{"type":"string"},"type":"array"},"bool":{"enum":[false],"type":"boolean"},"integer":{"enum":[1],"type":"integer"},"obj":{"properties":{"f0":{"enum":["v0"],"type":"string"},"f1":{"enum":["v1"],"type":"string"},"f2":{"enum":["v2"],"type":"string"}},"required":["f0","f1","f2"],"type":"object"},"string":{"enum":["a"],"type":"string"}},"required":["bool","string","obj","array","integer"],"type":"object"}`
|
||||
|
||||
s := fmt.Sprintf(`{"components":{"schemas":{"values":%s}}}`, testData)
|
||||
testSwagger, err := openapi3.NewLoader().LoadFromData([]byte(s))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
testSchema := testSwagger.Components.Schemas["values"].Value
|
||||
changeEnumToDefault(testSchema)
|
||||
result, err := testSchema.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
resultMap := map[string]interface{}{}
|
||||
err = json.Unmarshal(result, &resultMap)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
want := `{"properties":{"array":{"default":["a","b","c"],"items":{"type":"string"},"type":"array"},"bool":{"default":false,"type":"boolean"},"integer":{"default":1,"type":"integer"},"obj":{"properties":{"f0":{"default":"v0","type":"string"},"f1":{"default":"v1","type":"string"},"f2":{"default":"v2","type":"string"}},"type":"object"},"string":{"default":"a","type":"string"}},"type":"object"}`
|
||||
wantMap := map[string]interface{}{}
|
||||
_ = json.Unmarshal([]byte(want), &wantMap)
|
||||
if diff := cmp.Diff(resultMap, wantMap); diff != "" {
|
||||
t.Fatalf("\nchangeEnumToDefault(...) -want +get %s\n", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeSwaggerCompatible(t *testing.T) {
|
||||
tests := []struct {
|
||||
caseName string
|
||||
testdata string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
caseName: "integer type array",
|
||||
testdata: `{"integerArray": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
2
|
||||
]
|
||||
}
|
||||
],
|
||||
"enum": [
|
||||
[
|
||||
0,
|
||||
1,
|
||||
2
|
||||
]
|
||||
]
|
||||
}}`,
|
||||
want: `{"integerArray":{"enum":[[0,1,2]],"items":{"enum":null,"type":"integer"},"type":"array"}}`,
|
||||
},
|
||||
{
|
||||
caseName: "string type array",
|
||||
testdata: `{"stringArray": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"c"
|
||||
]
|
||||
}
|
||||
],
|
||||
"enum": [
|
||||
[
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
]
|
||||
]
|
||||
}}`,
|
||||
want: `{"stringArray":{"enum":[["a","b","c"]],"items":{"enum":null,"type":"string"},"type":"array"}}`,
|
||||
},
|
||||
{
|
||||
caseName: "bool type array",
|
||||
testdata: `{"boolArray": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"enum": [
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"enum": [
|
||||
false
|
||||
]
|
||||
}
|
||||
],
|
||||
"enum": [
|
||||
[
|
||||
true,
|
||||
false
|
||||
]
|
||||
]
|
||||
}}`,
|
||||
want: `{"boolArray":{"enum":[[true,false]],"items":{"enum":null,"type":"boolean"},"type":"array"}}`,
|
||||
},
|
||||
{
|
||||
caseName: "object type array",
|
||||
testdata: `{"objectArray": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"f0",
|
||||
"f1",
|
||||
"f2"
|
||||
],
|
||||
"properties": {
|
||||
"f0": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"v0"
|
||||
]
|
||||
},
|
||||
"f1": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"v1"
|
||||
]
|
||||
},
|
||||
"f2": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"v2"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}}`,
|
||||
want: `{"objectArray":{"items":{"enum":null,"properties":{"f0":{"enum":["v0"],"type":"string"},"f1":{"enum":["v1"],"type":"string"},"f2":{"enum":["v2"],"type":"string"}},"required":["f0","f1","f2"],"type":"object"},"type":"array"}}`,
|
||||
},
|
||||
{
|
||||
caseName: "object type array embeds object type array",
|
||||
testdata: `{
|
||||
"objectArray": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"f0"
|
||||
],
|
||||
"properties": {
|
||||
"f0": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"v0"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`,
|
||||
want: `{"objectArray":{"items":{"enum":null,"items":{"enum":null,"properties":{"f0":{"enum":["v0"],"type":"string"}},"required":["f0"],"type":"object"},"type":"array"},"type":"array"}}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.caseName, func(t *testing.T) {
|
||||
result, err := makeSwaggerCompatible([]byte(tc.testdata))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
resultMap := map[string]interface{}{}
|
||||
err = json.Unmarshal(result, &resultMap)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
wantMap := map[string]interface{}{}
|
||||
_ = json.Unmarshal([]byte(tc.want), &wantMap)
|
||||
if diff := cmp.Diff(resultMap, wantMap); diff != "" {
|
||||
t.Fatalf("\nmakeSwaggerCompatible(...) -want +get %s\n", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -54,8 +54,6 @@ type Template struct {
|
||||
CustomStatus string
|
||||
CapabilityCategory types.CapabilityCategory
|
||||
Reference common.WorkloadTypeDescriptor
|
||||
Helm *common.Helm
|
||||
Kube *common.Kube
|
||||
Terraform *common.Terraform
|
||||
|
||||
ComponentDefinition *v1beta1.ComponentDefinition
|
||||
@@ -362,16 +360,6 @@ func loadSchematicToTemplate(tmpl *Template, status *common.Status, schematic *c
|
||||
tmpl.CapabilityCategory = types.CUECategory
|
||||
tmpl.TemplateStr = schematic.CUE.Template
|
||||
}
|
||||
if schematic.HELM != nil {
|
||||
tmpl.CapabilityCategory = types.HelmCategory
|
||||
tmpl.Helm = schematic.HELM
|
||||
return nil
|
||||
}
|
||||
if schematic.KUBE != nil {
|
||||
tmpl.CapabilityCategory = types.KubeCategory
|
||||
tmpl.Kube = schematic.KUBE
|
||||
return nil
|
||||
}
|
||||
if schematic.Terraform != nil {
|
||||
tmpl.CapabilityCategory = types.TerraformCategory
|
||||
tmpl.Terraform = schematic.Terraform
|
||||
|
||||
@@ -293,20 +293,6 @@ func TestLoadSchematicToTemplate(t *testing.T) {
|
||||
Terraform: &common.Terraform{},
|
||||
},
|
||||
},
|
||||
"helm schematic": {
|
||||
schematic: &common.Schematic{HELM: &common.Helm{}},
|
||||
want: &Template{
|
||||
CapabilityCategory: types.HelmCategory,
|
||||
Helm: &common.Helm{},
|
||||
},
|
||||
},
|
||||
"kube schematic": {
|
||||
schematic: &common.Schematic{KUBE: &common.Kube{}},
|
||||
want: &Template{
|
||||
CapabilityCategory: types.KubeCategory,
|
||||
Kube: &common.Kube{},
|
||||
},
|
||||
},
|
||||
}
|
||||
for reason, casei := range testCases {
|
||||
gtmp := &Template{}
|
||||
@@ -365,8 +351,6 @@ spec:
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
Helm: nil,
|
||||
Kube: nil,
|
||||
ComponentDefinition: compDef,
|
||||
}
|
||||
|
||||
@@ -375,8 +359,6 @@ spec:
|
||||
Health: "testHealthPolicy",
|
||||
CustomStatus: "testCustomStatus",
|
||||
CapabilityCategory: types.CUECategory,
|
||||
Helm: nil,
|
||||
Kube: nil,
|
||||
TraitDefinition: traitDef,
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,11 @@ package auth
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/kubevela/pkg/util/net"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/client-go/transport"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -67,7 +66,7 @@ func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Respons
|
||||
}
|
||||
|
||||
func (rt *impersonatingRoundTripper) CancelRequest(req *http.Request) {
|
||||
utils.TryCancelRequest(rt.WrappedRoundTripper(), req)
|
||||
net.TryCancelRequest(rt.WrappedRoundTripper(), req)
|
||||
}
|
||||
|
||||
func (rt *impersonatingRoundTripper) WrappedRoundTripper() http.RoundTripper {
|
||||
|
||||
@@ -22,23 +22,18 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
pkgmulticluster "github.com/kubevela/pkg/multicluster"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
|
||||
configprovider "github.com/oam-dev/kubevela/pkg/config/provider"
|
||||
ctrlutil "github.com/oam-dev/kubevela/pkg/controller/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
|
||||
"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"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
monitorContext "github.com/kubevela/pkg/monitor/context"
|
||||
pkgmulticluster "github.com/kubevela/pkg/multicluster"
|
||||
"github.com/kubevela/pkg/util/slices"
|
||||
workflowv1alpha1 "github.com/kubevela/workflow/api/v1alpha1"
|
||||
"github.com/kubevela/workflow/pkg/cue/model/value"
|
||||
"github.com/kubevela/workflow/pkg/executor"
|
||||
@@ -53,15 +48,17 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/appfile"
|
||||
"github.com/oam-dev/kubevela/pkg/auth"
|
||||
configprovider "github.com/oam-dev/kubevela/pkg/config/provider"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/core.oam.dev/v1beta1/application/assemble"
|
||||
ctrlutil "github.com/oam-dev/kubevela/pkg/controller/utils"
|
||||
velaprocess "github.com/oam-dev/kubevela/pkg/cue/process"
|
||||
"github.com/oam-dev/kubevela/pkg/features"
|
||||
"github.com/oam-dev/kubevela/pkg/monitor/metrics"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/policy/envbinding"
|
||||
"github.com/oam-dev/kubevela/pkg/stdlib"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
"github.com/oam-dev/kubevela/pkg/velaql/providers/query"
|
||||
multiclusterProvider "github.com/oam-dev/kubevela/pkg/workflow/providers/multicluster"
|
||||
oamProvider "github.com/oam-dev/kubevela/pkg/workflow/providers/oam"
|
||||
@@ -308,7 +305,7 @@ func checkDependsOnValidComponent(dependsOnComponentNames, allComponentNames []s
|
||||
return "", true
|
||||
}
|
||||
for _, dc := range dependsOnComponentNames {
|
||||
if !utils.StringsContain(allComponentNames, dc) {
|
||||
if !slices.Contains(allComponentNames, dc) {
|
||||
return dc, false
|
||||
}
|
||||
}
|
||||
@@ -370,7 +367,6 @@ func (h *AppHandler) applyComponentFunc(appParser *appfile.Parser, appRev *v1bet
|
||||
ctx := multicluster.ContextWithClusterName(baseCtx, clusterName)
|
||||
ctx = contextWithComponentNamespace(ctx, overrideNamespace)
|
||||
ctx = contextWithReplicaKey(ctx, comp.ReplicaKey)
|
||||
ctx = envbinding.ContextWithEnvName(ctx, env)
|
||||
|
||||
wl, manifest, err := h.prepareWorkloadAndManifests(ctx, appParser, comp, appRev, patcher, af)
|
||||
if err != nil {
|
||||
|
||||
@@ -22,8 +22,6 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/oam/testutil"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
@@ -31,13 +29,11 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
crdv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/pointer"
|
||||
"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/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
@@ -319,100 +315,6 @@ spec:
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the ComponentDefinition contains Helm Module, should create a ConfigMap", func() {
|
||||
var componentDefinitionName = "cd-with-helm-module"
|
||||
var namespace = "default"
|
||||
req := reconcile.Request{NamespacedName: client.ObjectKey{Name: componentDefinitionName, Namespace: namespace}}
|
||||
|
||||
It("Applying ComponentDefinition with Helm module", func() {
|
||||
cd := v1beta1.ComponentDefinition{}
|
||||
cd.SetName(componentDefinitionName)
|
||||
cd.SetNamespace(namespace)
|
||||
cd.Spec.Workload.Definition = common.WorkloadGVK{APIVersion: "apps/v1", Kind: "Deployment"}
|
||||
cd.Spec.Schematic = &common.Schematic{
|
||||
HELM: &common.Helm{
|
||||
Release: *util.Object2RawExtension(map[string]interface{}{
|
||||
"chart": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"chart": "podinfo",
|
||||
"version": "5.1.4",
|
||||
},
|
||||
},
|
||||
}),
|
||||
Repository: *util.Object2RawExtension(map[string]interface{}{
|
||||
"url": "https://charts.kubevela.net/example/",
|
||||
}),
|
||||
},
|
||||
}
|
||||
By("Create ComponentDefinition")
|
||||
Expect(k8sClient.Create(ctx, &cd)).Should(Succeed())
|
||||
testutil.ReconcileRetry(&r, req)
|
||||
|
||||
By("Check whether ConfigMap is created")
|
||||
var cm corev1.ConfigMap
|
||||
name := fmt.Sprintf("component-%s%s", types.CapabilityConfigMapNamePrefix, componentDefinitionName)
|
||||
Eventually(func() bool {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, &cm)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to get configmap")
|
||||
}
|
||||
return err == nil
|
||||
}, 10*time.Second, time.Second).Should(BeTrue())
|
||||
Expect(cm.Data[types.OpenapiV3JSONSchema]).Should(Not(Equal("")))
|
||||
|
||||
By("Check whether ConfigMapRef refer to right ConfigMap")
|
||||
Eventually(func() string {
|
||||
_ = k8sClient.Get(ctx, client.ObjectKey{Namespace: cd.Namespace, Name: cd.Name}, &cd)
|
||||
return cd.Status.ConfigMapRef
|
||||
}, 10*time.Second, time.Second).Should(Equal(name))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the ComponentDefinition contains Kube Module, should create a ConfigMap", func() {
|
||||
var componentDefinitionName = "cd-with-kube-module"
|
||||
var namespace = "default"
|
||||
req := reconcile.Request{NamespacedName: client.ObjectKey{Name: componentDefinitionName, Namespace: namespace}}
|
||||
|
||||
It("Applying ComponentDefinition with kube Module", func() {
|
||||
cd := v1beta1.ComponentDefinition{}
|
||||
cd.SetName(componentDefinitionName)
|
||||
cd.SetNamespace(namespace)
|
||||
cd.Spec.Workload.Definition = common.WorkloadGVK{APIVersion: "apps/v1", Kind: "Deployment"}
|
||||
cd.Spec.Schematic = &common.Schematic{
|
||||
KUBE: &common.Kube{
|
||||
Template: generateTemplate(KUBEWorkerTemplate),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
Required: pointer.Bool(true),
|
||||
Description: pointer.String("test description"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Create ComponentDefinition")
|
||||
Expect(k8sClient.Create(ctx, &cd)).Should(Succeed())
|
||||
testutil.ReconcileRetry(&r, req)
|
||||
|
||||
By("Check whether ConfigMap is created")
|
||||
var cm corev1.ConfigMap
|
||||
name := fmt.Sprintf("component-%s%s", types.CapabilityConfigMapNamePrefix, componentDefinitionName)
|
||||
Eventually(func() bool {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, &cm)
|
||||
return err == nil
|
||||
}, 10*time.Second, time.Second).Should(BeTrue())
|
||||
Expect(cm.Data[types.OpenapiV3JSONSchema]).Should(Not(Equal("")))
|
||||
|
||||
By("Check whether ConfigMapRef refer to right ConfigMap")
|
||||
Eventually(func() string {
|
||||
_ = k8sClient.Get(ctx, client.ObjectKey{Namespace: cd.Namespace, Name: cd.Name}, &cd)
|
||||
return cd.Status.ConfigMapRef
|
||||
}, 10*time.Second, time.Second).Should(Equal(name))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When the ComponentDefinition contains Terraform Module, should create a ConfigMap", func() {
|
||||
var componentDefinitionName = "alibaba-rds-test"
|
||||
var namespace = "default"
|
||||
@@ -740,25 +642,3 @@ spec:
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func generateTemplate(template string) runtime.RawExtension {
|
||||
b, _ := yaml.YAMLToJSON([]byte(template))
|
||||
return runtime.RawExtension{Raw: b}
|
||||
}
|
||||
|
||||
var KUBEWorkerTemplate = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
`
|
||||
|
||||
@@ -1,51 +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 utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/client-go/util/retry"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
// FreezeApplication freeze application to disable the reconciling process for it
|
||||
func FreezeApplication(ctx context.Context, cli client.Client, app *v1beta1.Application, mutate func()) (string, error) {
|
||||
return oam.GetControllerRequirement(app), _updateApplicationWithControllerRequirement(ctx, cli, app, mutate, "Disabled")
|
||||
}
|
||||
|
||||
// UnfreezeApplication unfreeze application to enable the reconciling process for it
|
||||
func UnfreezeApplication(ctx context.Context, cli client.Client, app *v1beta1.Application, mutate func(), originalControllerRequirement string) error {
|
||||
return _updateApplicationWithControllerRequirement(ctx, cli, app, mutate, originalControllerRequirement)
|
||||
}
|
||||
|
||||
func _updateApplicationWithControllerRequirement(ctx context.Context, cli client.Client, app *v1beta1.Application, mutate func(), controllerRequirement string) error {
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err := cli.Get(ctx, appKey, app); err != nil {
|
||||
return err
|
||||
}
|
||||
oam.SetControllerRequirement(app, controllerRequirement)
|
||||
if mutate != nil {
|
||||
mutate()
|
||||
}
|
||||
return cli.Update(ctx, app)
|
||||
})
|
||||
}
|
||||
@@ -40,7 +40,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/appfile"
|
||||
"github.com/oam-dev/kubevela/pkg/appfile/helm"
|
||||
"github.com/oam-dev/kubevela/pkg/cue/script"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
@@ -99,8 +98,6 @@ type CapabilityComponentDefinition struct {
|
||||
WorkloadType util.WorkloadType `json:"workloadType"`
|
||||
WorkloadDefName string `json:"workloadDefName"`
|
||||
|
||||
Helm *commontypes.Helm `json:"helm"`
|
||||
Kube *commontypes.Kube `json:"kube"`
|
||||
Terraform *commontypes.Terraform `json:"terraform"`
|
||||
CapabilityBaseDefinition
|
||||
}
|
||||
@@ -114,14 +111,6 @@ func NewCapabilityComponentDef(componentDefinition *v1beta1.ComponentDefinition)
|
||||
def.WorkloadDefName = componentDefinition.Spec.Workload.Type
|
||||
}
|
||||
if componentDefinition.Spec.Schematic != nil {
|
||||
if componentDefinition.Spec.Schematic.HELM != nil {
|
||||
def.WorkloadType = util.HELMDef
|
||||
def.Helm = componentDefinition.Spec.Schematic.HELM
|
||||
}
|
||||
if componentDefinition.Spec.Schematic.KUBE != nil {
|
||||
def.WorkloadType = util.KubeDef
|
||||
def.Kube = componentDefinition.Spec.Schematic.KUBE
|
||||
}
|
||||
if componentDefinition.Spec.Schematic.Terraform != nil {
|
||||
def.WorkloadType = util.TerraformDef
|
||||
def.Terraform = componentDefinition.Spec.Schematic.Terraform
|
||||
@@ -329,37 +318,6 @@ func generateJSONSchemaWithRequiredProperty(schemas map[string]*openapi3.Schema,
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// GetKubeSchematicOpenAPISchema gets OpenAPI v3 schema based on kube schematic parameters for component and trait definition
|
||||
func GetKubeSchematicOpenAPISchema(params []commontypes.KubeParameter) ([]byte, error) {
|
||||
required := []string{}
|
||||
properties := map[string]*openapi3.Schema{}
|
||||
for _, p := range params {
|
||||
var tmp *openapi3.Schema
|
||||
switch p.ValueType {
|
||||
case commontypes.StringType:
|
||||
tmp = openapi3.NewStringSchema()
|
||||
case commontypes.NumberType:
|
||||
tmp = openapi3.NewFloat64Schema()
|
||||
case commontypes.BooleanType:
|
||||
tmp = openapi3.NewBoolSchema()
|
||||
default:
|
||||
tmp = openapi3.NewStringSchema()
|
||||
}
|
||||
if p.Required != nil && *p.Required {
|
||||
required = append(required, p.Name)
|
||||
}
|
||||
|
||||
if p.Description != nil {
|
||||
tmp.Description = fmt.Sprintf("%s %s", tmp.Description, *p.Description)
|
||||
} else {
|
||||
// save FieldPaths into description
|
||||
tmp.Description = fmt.Sprintf("The value will be applied to fields: [%s].", strings.Join(p.FieldPaths, ","))
|
||||
}
|
||||
properties[p.Name] = tmp
|
||||
}
|
||||
return generateJSONSchemaWithRequiredProperty(properties, required)
|
||||
}
|
||||
|
||||
// GetGitSSHPublicKey gets a kubernetes secret containing the SSH private key based on gitCredentialsSecretReference parameters for component and trait definition
|
||||
func GetGitSSHPublicKey(ctx context.Context, k8sClient client.Client, gitCredentialsSecretReference *v1.SecretReference) (*gitssh.PublicKeys, error) {
|
||||
gitCredentialsSecretName := gitCredentialsSecretReference.Name
|
||||
@@ -403,10 +361,6 @@ func (def *CapabilityComponentDefinition) StoreOpenAPISchema(ctx context.Context
|
||||
var jsonSchema []byte
|
||||
var err error
|
||||
switch def.WorkloadType {
|
||||
case util.HELMDef:
|
||||
jsonSchema, err = helm.GetChartValuesJSONSchema(ctx, def.Helm)
|
||||
case util.KubeDef:
|
||||
jsonSchema, err = GetKubeSchematicOpenAPISchema(def.Kube.Parameters)
|
||||
case util.TerraformDef:
|
||||
if def.Terraform == nil {
|
||||
return "", fmt.Errorf("no Configuration is set in Terraform specification: %s", def.Name)
|
||||
@@ -475,8 +429,6 @@ type CapabilityTraitDefinition struct {
|
||||
|
||||
DefCategoryType util.WorkloadType `json:"defCategoryType"`
|
||||
|
||||
Kube *commontypes.Kube `json:"kube"`
|
||||
|
||||
CapabilityBaseDefinition
|
||||
}
|
||||
|
||||
@@ -484,10 +436,6 @@ type CapabilityTraitDefinition struct {
|
||||
func NewCapabilityTraitDef(traitdefinition *v1beta1.TraitDefinition) CapabilityTraitDefinition {
|
||||
var def CapabilityTraitDefinition
|
||||
def.Name = traitdefinition.Name // or def.Name = req.NamespacedName.Name
|
||||
if traitdefinition.Spec.Schematic != nil && traitdefinition.Spec.Schematic.KUBE != nil {
|
||||
def.DefCategoryType = util.KubeDef
|
||||
def.Kube = traitdefinition.Spec.Schematic.KUBE
|
||||
}
|
||||
def.TraitDefinition = *traitdefinition.DeepCopy()
|
||||
return def
|
||||
}
|
||||
@@ -503,14 +451,7 @@ func (def *CapabilityTraitDefinition) GetOpenAPISchema(name string) ([]byte, err
|
||||
|
||||
// StoreOpenAPISchema stores OpenAPI v3 schema from TraitDefinition in ConfigMap
|
||||
func (def *CapabilityTraitDefinition) StoreOpenAPISchema(ctx context.Context, k8sClient client.Client, namespace, name string, revName string) (string, error) {
|
||||
var jsonSchema []byte
|
||||
var err error
|
||||
switch def.DefCategoryType {
|
||||
case util.KubeDef: // Kube template
|
||||
jsonSchema, err = GetKubeSchematicOpenAPISchema(def.Kube.Parameters)
|
||||
default: // CUE template
|
||||
jsonSchema, err = def.GetOpenAPISchema(name)
|
||||
}
|
||||
jsonSchema, err := def.GetOpenAPISchema(name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate OpenAPI v3 JSON schema for capability %s: %w", def.Name, err)
|
||||
}
|
||||
|
||||
@@ -20,23 +20,13 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/hashstructure/v2"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
)
|
||||
|
||||
// DefaultBackoff is the backoff we use in controller
|
||||
var DefaultBackoff = wait.Backoff{
|
||||
Duration: 1 * time.Second,
|
||||
Factor: 2,
|
||||
Steps: 5,
|
||||
Jitter: 0.1,
|
||||
}
|
||||
|
||||
// GetAppNextRevision will generate the next revision name and revision number for application
|
||||
func GetAppNextRevision(app *v1beta1.Application) (string, int64) {
|
||||
if app == nil {
|
||||
|
||||
@@ -41,10 +41,7 @@ import (
|
||||
ocmclusterv1 "open-cluster-management.io/api/cluster/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/policy/envbinding"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
velaerrors "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
@@ -597,25 +594,5 @@ func getMutableClusterSecret(ctx context.Context, c client.Client, clusterName s
|
||||
if labels == nil || labels[clustercommon.LabelKeyClusterCredentialType] == "" {
|
||||
return nil, fmt.Errorf("invalid cluster secret %s: cluster credential type label %s is not set", clusterName, clustercommon.LabelKeyClusterCredentialType)
|
||||
}
|
||||
apps := &v1beta1.ApplicationList{}
|
||||
if err := c.List(ctx, apps); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to find applications to check clusters")
|
||||
}
|
||||
errs := velaerrors.ErrorList{}
|
||||
for _, app := range apps.Items {
|
||||
status, err := envbinding.GetEnvBindingPolicyStatus(app.DeepCopy(), "")
|
||||
if err == nil && status != nil {
|
||||
for _, env := range status.Envs {
|
||||
for _, placement := range env.Placements {
|
||||
if placement.Cluster == clusterName {
|
||||
errs = append(errs, fmt.Errorf("application %s/%s (env: %s) is currently using cluster %s", app.Namespace, app.Name, env.Env, clusterName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if errs.HasError() {
|
||||
return nil, errors.Wrapf(errs, "cluster %s is in use now", clusterName)
|
||||
}
|
||||
return clusterSecret, nil
|
||||
}
|
||||
|
||||
@@ -35,16 +35,6 @@ func ReconcileRetry(r reconcile.Reconciler, req reconcile.Request) {
|
||||
}, 15*time.Second, time.Second).Should(gomega.BeNil())
|
||||
}
|
||||
|
||||
// ReconcileRetryAndExpectErr will reconcile and get error
|
||||
func ReconcileRetryAndExpectErr(r reconcile.Reconciler, req reconcile.Request) {
|
||||
gomega.Eventually(func() error {
|
||||
if _, err := r.Reconcile(context.TODO(), req); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, 3*time.Second, time.Second).ShouldNot(gomega.BeNil())
|
||||
}
|
||||
|
||||
// ReconcileOnce will just reconcile once
|
||||
func ReconcileOnce(r reconcile.Reconciler, req reconcile.Request) {
|
||||
if _, err := r.Reconcile(context.TODO(), req); err != nil {
|
||||
|
||||
@@ -18,7 +18,6 @@ package envbinding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@@ -200,19 +199,3 @@ func PatchComponents(baseComponents []common.ApplicationComponent, patchComponen
|
||||
}
|
||||
return newComponents, nil
|
||||
}
|
||||
|
||||
// PatchApplicationByEnvBindingEnv get patched application directly through policyName and envName
|
||||
func PatchApplicationByEnvBindingEnv(app *v1beta1.Application, policyName string, envName string) (*v1beta1.Application, error) {
|
||||
policy, err := GetEnvBindingPolicy(app, policyName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policy != nil {
|
||||
for _, env := range policy.Envs {
|
||||
if env.Name == envName {
|
||||
return PatchApplication(app, &env.Patch, env.Selector)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("target env %s in policy %s not found", envName, policyName)
|
||||
}
|
||||
|
||||
@@ -26,21 +26,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
)
|
||||
|
||||
// ReadPlacementDecisions read placement decisions from application status, return (decisions, if decision is made, error)
|
||||
// Deprecated As it is only used in EnvBinding policy
|
||||
func ReadPlacementDecisions(app *v1beta1.Application, policyName string, envName string) ([]v1alpha1.PlacementDecision, bool, error) {
|
||||
envBindingStatus, err := GetEnvBindingPolicyStatus(app, policyName)
|
||||
if err != nil || envBindingStatus == nil {
|
||||
return nil, false, err
|
||||
}
|
||||
for _, envStatus := range envBindingStatus.Envs {
|
||||
if envStatus.Env == envName {
|
||||
return envStatus.Placements, true, nil
|
||||
}
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// updateClusterConnections update cluster connection in envbinding status with decisions
|
||||
func updateClusterConnections(status *v1alpha1.EnvBindingStatus, decisions []v1alpha1.PlacementDecision, app *v1beta1.Application) {
|
||||
var currentRev string
|
||||
|
||||
@@ -17,83 +17,15 @@ limitations under the License.
|
||||
package envbinding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
)
|
||||
|
||||
func TestReadPlacementDecisions(t *testing.T) {
|
||||
pld := []v1alpha1.PlacementDecision{{
|
||||
Cluster: "example-cluster",
|
||||
Namespace: "example-namespace",
|
||||
}}
|
||||
testCases := []struct {
|
||||
Status *v1alpha1.EnvBindingStatus
|
||||
StatusRaw []byte
|
||||
ExpectedExists bool
|
||||
ExpectedHasError bool
|
||||
}{{
|
||||
Status: nil,
|
||||
StatusRaw: []byte(`bad value`),
|
||||
ExpectedExists: false,
|
||||
ExpectedHasError: true,
|
||||
}, {
|
||||
Status: &v1alpha1.EnvBindingStatus{
|
||||
Envs: []v1alpha1.EnvStatus{{
|
||||
Env: "example-env",
|
||||
Placements: pld,
|
||||
}},
|
||||
},
|
||||
ExpectedExists: true,
|
||||
ExpectedHasError: false,
|
||||
}, {
|
||||
Status: &v1alpha1.EnvBindingStatus{
|
||||
Envs: []v1alpha1.EnvStatus{{
|
||||
Env: "bad-env",
|
||||
Placements: pld,
|
||||
}},
|
||||
},
|
||||
ExpectedExists: false,
|
||||
ExpectedHasError: false,
|
||||
}}
|
||||
r := require.New(t)
|
||||
for _, testCase := range testCases {
|
||||
app := &v1beta1.Application{}
|
||||
_status := common.PolicyStatus{
|
||||
Name: "example-policy",
|
||||
Type: v1alpha1.EnvBindingPolicyType,
|
||||
}
|
||||
if testCase.Status == nil {
|
||||
_status.Status = &runtime.RawExtension{Raw: testCase.StatusRaw}
|
||||
} else {
|
||||
bs, err := json.Marshal(testCase.Status)
|
||||
r.NoError(err)
|
||||
_status.Status = &runtime.RawExtension{Raw: bs}
|
||||
}
|
||||
app.Status.PolicyStatus = []common.PolicyStatus{_status}
|
||||
pds, exists, err := ReadPlacementDecisions(app, "", "example-env")
|
||||
r.Equal(testCase.ExpectedExists, exists)
|
||||
if testCase.ExpectedHasError {
|
||||
r.Error(err)
|
||||
continue
|
||||
}
|
||||
r.NoError(err)
|
||||
if exists {
|
||||
r.Equal(len(pld), len(pds))
|
||||
for idx := range pld {
|
||||
r.Equal(pld[idx].Cluster, pds[idx].Cluster)
|
||||
r.Equal(pld[idx].Namespace, pds[idx].Namespace)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateClusterConnections(t *testing.T) {
|
||||
app := &v1beta1.Application{}
|
||||
app.Status.LatestRevision = &common.Revision{Name: "v1"}
|
||||
|
||||
@@ -1,78 +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 envbinding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
)
|
||||
|
||||
type contextKey string
|
||||
|
||||
const (
|
||||
// EnvNameContextKey is the name of env
|
||||
// Deprecated
|
||||
EnvNameContextKey = contextKey("EnvName")
|
||||
)
|
||||
|
||||
// GetEnvBindingPolicy extract env-binding policy with given policy name, if policy name is empty, the first env-binding policy will be used
|
||||
// Deprecated
|
||||
func GetEnvBindingPolicy(app *v1beta1.Application, policyName string) (*v1alpha1.EnvBindingSpec, error) {
|
||||
for _, policy := range app.Spec.Policies {
|
||||
if (policy.Name == policyName || policyName == "") && policy.Type == v1alpha1.EnvBindingPolicyType && policy.Properties != nil {
|
||||
envBindingSpec := &v1alpha1.EnvBindingSpec{}
|
||||
err := json.Unmarshal(policy.Properties.Raw, envBindingSpec)
|
||||
return envBindingSpec, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetEnvBindingPolicyStatus extract env-binding policy status with given policy name, if policy name is empty, the first env-binding policy will be used
|
||||
// Deprecated
|
||||
func GetEnvBindingPolicyStatus(app *v1beta1.Application, policyName string) (*v1alpha1.EnvBindingStatus, error) {
|
||||
for _, policyStatus := range app.Status.PolicyStatus {
|
||||
if (policyStatus.Name == policyName || policyName == "") && policyStatus.Type == v1alpha1.EnvBindingPolicyType {
|
||||
envBindingStatus := &v1alpha1.EnvBindingStatus{}
|
||||
if policyStatus.Status != nil {
|
||||
err := json.Unmarshal(policyStatus.Status.Raw, envBindingStatus)
|
||||
return envBindingStatus, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// EnvNameInContext extract env name from context
|
||||
// Deprecated
|
||||
func EnvNameInContext(ctx context.Context) string {
|
||||
envName := ctx.Value(EnvNameContextKey)
|
||||
if envName != nil {
|
||||
return envName.(string)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ContextWithEnvName wraps context with envName
|
||||
// Deprecated
|
||||
func ContextWithEnvName(ctx context.Context, envName string) context.Context {
|
||||
return context.WithValue(ctx, EnvNameContextKey, envName)
|
||||
}
|
||||
@@ -1,32 +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 envbinding
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEnvContext(t *testing.T) {
|
||||
r := require.New(t)
|
||||
envName := "env"
|
||||
extracted := EnvNameInContext(ContextWithEnvName(context.Background(), envName))
|
||||
r.Equal(envName, extracted)
|
||||
r.Equal("", EnvNameInContext(context.Background()))
|
||||
}
|
||||
@@ -84,7 +84,8 @@ func TestResourceCache(t *testing.T) {
|
||||
}
|
||||
rts := []*v1beta1.ResourceTracker{nil, rt1, rt2, rt3}
|
||||
cache.registerResourceTrackers(rts...)
|
||||
o, ok := cache.m.Get(createMR("resource-1").ResourceKey())
|
||||
mr := createMR("resource-1")
|
||||
o, ok := cache.m.Get(mr.ResourceKey())
|
||||
r.True(ok)
|
||||
r.False(o.loaded)
|
||||
for _, check := range []struct {
|
||||
|
||||
@@ -331,7 +331,7 @@ func (h *gcHandler) deleteIndependentComponent(ctx context.Context, mr v1beta1.M
|
||||
} else {
|
||||
dependentClear := true
|
||||
for _, mr := range rt.Spec.ManagedResources {
|
||||
if utils.StringsContain(dependent, mr.Component) {
|
||||
if slices.Contains(dependent, mr.Component) {
|
||||
entry := h.cache.get(ctx, mr)
|
||||
if entry.gcExecutorRT != rt {
|
||||
continue
|
||||
@@ -435,7 +435,7 @@ func (h *gcHandler) checkDependentComponent(mr v1beta1.ManagedResource) []string
|
||||
}
|
||||
for _, comp := range h.app.Spec.Components {
|
||||
for _, input := range comp.Inputs {
|
||||
if utils.StringsContain(outputs, input.From) {
|
||||
if slices.Contains(outputs, input.From) {
|
||||
dependent = append(dependent, comp.Name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,21 +30,6 @@
|
||||
dependsOn?: [...string]
|
||||
}
|
||||
|
||||
// deprecated
|
||||
#ReadPlacementDecisions: {
|
||||
#provider: "multicluster"
|
||||
#do: "read-placement-decisions"
|
||||
|
||||
inputs: {
|
||||
policyName: string
|
||||
envName: string
|
||||
}
|
||||
|
||||
outputs: {
|
||||
decisions?: [...#PlacementDecision]
|
||||
}
|
||||
}
|
||||
|
||||
// deprecated
|
||||
#MakePlacementDecisions: {
|
||||
#provider: "multicluster"
|
||||
|
||||
@@ -84,7 +84,7 @@ func RollbackApplicationWithRevision(ctx context.Context, cli client.Client, app
|
||||
|
||||
// freeze the application
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
controllerRequirement, err := utils.FreezeApplication(ctx, cli, app, func() {
|
||||
controllerRequirement, err := FreezeApplication(ctx, cli, app, func() {
|
||||
app.Spec = matchedRev.Spec.Application.Spec
|
||||
oam.SetPublishVersion(app, publishVersion)
|
||||
})
|
||||
@@ -94,7 +94,7 @@ func RollbackApplicationWithRevision(ctx context.Context, cli client.Client, app
|
||||
|
||||
defer func() {
|
||||
// unfreeze application
|
||||
if err = utils.UnfreezeApplication(ctx, cli, app, nil, controllerRequirement); err != nil {
|
||||
if err = UnfreezeApplication(ctx, cli, app, nil, controllerRequirement); err != nil {
|
||||
klog.Errorf("failed to unfreeze application %s after update:%s", appKey, err.Error())
|
||||
}
|
||||
}()
|
||||
@@ -130,3 +130,27 @@ func RollbackApplicationWithRevision(ctx context.Context, cli client.Client, app
|
||||
}
|
||||
return matchedRev, app, nil
|
||||
}
|
||||
|
||||
// FreezeApplication freeze application to disable the reconciling process for it
|
||||
func FreezeApplication(ctx context.Context, cli client.Client, app *v1beta1.Application, mutate func()) (string, error) {
|
||||
return oam.GetControllerRequirement(app), _updateApplicationWithControllerRequirement(ctx, cli, app, mutate, "Disabled")
|
||||
}
|
||||
|
||||
// UnfreezeApplication unfreeze application to enable the reconciling process for it
|
||||
func UnfreezeApplication(ctx context.Context, cli client.Client, app *v1beta1.Application, mutate func(), originalControllerRequirement string) error {
|
||||
return _updateApplicationWithControllerRequirement(ctx, cli, app, mutate, originalControllerRequirement)
|
||||
}
|
||||
|
||||
func _updateApplicationWithControllerRequirement(ctx context.Context, cli client.Client, app *v1beta1.Application, mutate func(), controllerRequirement string) error {
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
return retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
if err := cli.Get(ctx, appKey, app); err != nil {
|
||||
return err
|
||||
}
|
||||
oam.SetControllerRequirement(app, controllerRequirement)
|
||||
if mutate != nil {
|
||||
mutate()
|
||||
}
|
||||
return cli.Update(ctx, app)
|
||||
})
|
||||
}
|
||||
@@ -16,20 +16,6 @@ limitations under the License.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ResourceTrackerNotExistError identifies error caused by resourcetracker not exist
|
||||
type ResourceTrackerNotExistError struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// Error implement error interface
|
||||
func (err ResourceTrackerNotExistError) Error() string {
|
||||
return fmt.Sprintf("given resource tracker %q doesn't exist", err.Name)
|
||||
}
|
||||
|
||||
// ManagedResourceHasNoDataError identifies error caused by no data in maanged resource
|
||||
type ManagedResourceHasNoDataError struct{}
|
||||
|
||||
|
||||
@@ -19,32 +19,21 @@ package helm
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
types2 "k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
)
|
||||
|
||||
// VelaDebugLog defines an ENV to set vela helm install log to be debug
|
||||
const (
|
||||
VelaDebugLog = "VELA_DEBUG"
|
||||
userNameSecKey = "username"
|
||||
userPasswordSecKey = "password"
|
||||
caFileSecKey = "caFile"
|
||||
@@ -56,138 +45,6 @@ var (
|
||||
settings = cli.New()
|
||||
)
|
||||
|
||||
// Install will install helm chart
|
||||
func Install(ioStreams cmdutil.IOStreams, repoName, repoURL, chartName, version, namespace, releaseName string,
|
||||
vals map[string]interface{}) error {
|
||||
|
||||
if len(namespace) > 0 {
|
||||
args, err := common.InitBaseRestConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kubeClient, err := args.GetClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exist, err := cmdutil.DoesNamespaceExist(kubeClient, namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
if err = cmdutil.NewNamespace(kubeClient, namespace); err != nil {
|
||||
return fmt.Errorf("create namespace (%s) failed for chart %s: %w", namespace, chartName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// check release running first before add repo and install chart
|
||||
if IsHelmReleaseRunning(releaseName, chartName, namespace, ioStreams) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !IsHelmRepositoryExist(repoName, repoURL) {
|
||||
err := AddHelmRepository(repoName, repoURL,
|
||||
"", "", "", "", "", false, ioStreams.Out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
chartClient, err := NewHelmInstall(version, namespace, releaseName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chartRequested, err := GetChart(chartClient, repoName+"/"+chartName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel, err := chartClient.Run(chartRequested, vals)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ioStreams.Infof("Successfully installed chart (%s) with release name (%s)\n", chartName, rel.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Uninstall will uninstall helm chart
|
||||
func Uninstall(ioStreams cmdutil.IOStreams, chartName, namespace, releaseName string) error {
|
||||
if !IsHelmReleaseRunning(releaseName, chartName, namespace, ioStreams) {
|
||||
return nil
|
||||
}
|
||||
uninstall, err := NewHelmUninstall(namespace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = uninstall.Run(releaseName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ioStreams.Infof("Successfully removed chart (%s) with release name (%s)\n", chartName, releaseName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewHelmInstall will create a install client for helm install
|
||||
func NewHelmInstall(version, namespace, releaseName string) (*action.Install, error) {
|
||||
actionConfig := new(action.Configuration)
|
||||
if len(namespace) == 0 {
|
||||
namespace = types.DefaultKubeVelaNS
|
||||
}
|
||||
if err := actionConfig.Init(
|
||||
cmdutil.NewRestConfigGetter(namespace),
|
||||
namespace,
|
||||
os.Getenv("HELM_DRIVER"),
|
||||
debug,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := action.NewInstall(actionConfig)
|
||||
client.ReleaseName = releaseName
|
||||
// MUST set here, client didn't use namespace from configuration
|
||||
client.Namespace = namespace
|
||||
|
||||
if len(version) > 0 {
|
||||
client.Version = version
|
||||
} else {
|
||||
client.Version = types.DefaultKubeVelaVersion
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// NewHelmUninstall will create a helm uninstall client
|
||||
func NewHelmUninstall(namespace string) (*action.Uninstall, error) {
|
||||
actionConfig := new(action.Configuration)
|
||||
|
||||
if err := actionConfig.Init(
|
||||
cmdutil.NewRestConfigGetter(namespace),
|
||||
namespace,
|
||||
os.Getenv("HELM_DRIVER"),
|
||||
debug,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return action.NewUninstall(actionConfig), nil
|
||||
}
|
||||
|
||||
// IsHelmRepositoryExist will check help repo exists
|
||||
func IsHelmRepositoryExist(name, url string) bool {
|
||||
repos := GetHelmRepositoryList()
|
||||
for _, repo := range repos {
|
||||
if repo.Name == name && repo.URL == url {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetHelmRepositoryList get the helm repo list from default setting
|
||||
func GetHelmRepositoryList() []*repo.Entry {
|
||||
f, err := repo.LoadFile(settings.RepositoryConfig)
|
||||
if err == nil && len(f.Repositories) > 0 {
|
||||
return f.Repositories
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func debug(format string, v ...interface{}) {
|
||||
if settings.Debug {
|
||||
format = fmt.Sprintf("[debug] %s\n", format)
|
||||
@@ -195,53 +52,6 @@ func debug(format string, v ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// AddHelmRepository add helm repo
|
||||
func AddHelmRepository(name, url, username, password, certFile, keyFile, caFile string, insecureSkipTLSverify bool, out io.Writer) error {
|
||||
var f repo.File
|
||||
c := repo.Entry{
|
||||
Name: name,
|
||||
URL: url,
|
||||
Username: username,
|
||||
Password: password,
|
||||
CertFile: certFile,
|
||||
KeyFile: keyFile,
|
||||
CAFile: caFile,
|
||||
InsecureSkipTLSverify: insecureSkipTLSverify,
|
||||
}
|
||||
|
||||
r, err := repo.NewChartRepository(&c, getter.All(settings))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := r.DownloadIndexFile(); err != nil {
|
||||
return errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", url)
|
||||
}
|
||||
|
||||
f.Update(&c)
|
||||
|
||||
if err := f.WriteFile(settings.RepositoryConfig, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(out, "%q has been added to your repositories\n", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsHelmReleaseRunning check helm release running
|
||||
func IsHelmReleaseRunning(releaseName, chartName, ns string, streams cmdutil.IOStreams) bool {
|
||||
releases, err := GetHelmRelease(ns)
|
||||
if err != nil {
|
||||
streams.Error("get helm release err", err)
|
||||
return false
|
||||
}
|
||||
for _, r := range releases {
|
||||
if strings.Contains(r.Chart.ChartFullPath(), chartName) && r.Name == releaseName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetHelmRelease will get helm release
|
||||
func GetHelmRelease(ns string) ([]*release.Release, error) {
|
||||
actionConfig := new(action.Configuration)
|
||||
@@ -258,29 +68,6 @@ func GetHelmRelease(ns string) ([]*release.Release, error) {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// GetChart will locate chart
|
||||
func GetChart(client *action.Install, name string) (*chart.Chart, error) {
|
||||
if os.Getenv(VelaDebugLog) != "" {
|
||||
settings.Debug = true
|
||||
}
|
||||
|
||||
chartPath, err := client.ChartPathOptions.LocateChart(name, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chartRequested, err := loader.Load(chartPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return chartRequested, nil
|
||||
}
|
||||
|
||||
// InstallHelmChart will install helm chart from types.Chart
|
||||
func InstallHelmChart(ioStreams cmdutil.IOStreams, c types.Chart) error {
|
||||
return Install(ioStreams, c.Repo, c.URL, c.Name, c.Version, c.Namespace, c.Name, c.Values)
|
||||
}
|
||||
|
||||
// SetHTTPOption will read username and password from secret return a httpOption that contain these info.
|
||||
func SetHTTPOption(ctx context.Context, k8sClient client.Client, secretRef types2.NamespacedName) (*common.HTTPOption, error) {
|
||||
sec := v1.Secret{}
|
||||
|
||||
@@ -21,12 +21,6 @@ import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// DumpJSON returns the JSON encoding
|
||||
func DumpJSON(o interface{}) string {
|
||||
j, _ := json.Marshal(o)
|
||||
return string(j)
|
||||
}
|
||||
|
||||
// StrictUnmarshal unmarshal target structure and disallow unknown fields
|
||||
func StrictUnmarshal(bs []byte, dest interface{}) error {
|
||||
d := json.NewDecoder(bytes.NewReader(bs))
|
||||
|
||||
@@ -185,14 +185,6 @@ func GetUserInfoFromConfig(cfg *rest.Config) *authv1.UserInfo {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AutoSetSelfImpersonationInConfig set impersonate username and group to the identity in the original rest config
|
||||
func AutoSetSelfImpersonationInConfig(cfg *rest.Config) {
|
||||
if userInfo := GetUserInfoFromConfig(cfg); userInfo != nil {
|
||||
cfg.Impersonate.UserName = userInfo.Username
|
||||
cfg.Impersonate.Groups = append(cfg.Impersonate.Groups, userInfo.Groups...)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateOrUpdate create or update a kubernetes object
|
||||
func CreateOrUpdate(ctx context.Context, cli client.Client, obj client.Object) (controllerutil.OperationResult, error) {
|
||||
bs, err := json.Marshal(obj)
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
Copyright 2022 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 utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// EnablePprof listen to the pprofAddr and export the profiling results
|
||||
// If the errChan is nil, this function will panic when the listening error occurred.
|
||||
func EnablePprof(pprofAddr string, errChan chan error) {
|
||||
// Start pprof server if enabled
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
mux.HandleFunc("/mem/stat", func(writer http.ResponseWriter, request *http.Request) {
|
||||
var ms runtime.MemStats
|
||||
runtime.ReadMemStats(&ms)
|
||||
bs, _ := json.Marshal(ms)
|
||||
_, _ = writer.Write(bs)
|
||||
})
|
||||
mux.HandleFunc("/gc", func(writer http.ResponseWriter, request *http.Request) {
|
||||
runtime.GC()
|
||||
})
|
||||
pprofServer := http.Server{
|
||||
Addr: pprofAddr,
|
||||
Handler: mux,
|
||||
ReadHeaderTimeout: 2 * time.Second,
|
||||
}
|
||||
|
||||
klog.InfoS("Starting debug HTTP server", "addr", pprofServer.Addr)
|
||||
|
||||
if err := pprofServer.ListenAndServe(); err != nil {
|
||||
klog.Error(err, "Failed to start debug HTTP server")
|
||||
if errChan != nil {
|
||||
errChan <- err
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +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 utils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// TryCancelRequest tries to cancel the request by traversing round trippers
|
||||
func TryCancelRequest(rt http.RoundTripper, req *http.Request) {
|
||||
type canceler interface {
|
||||
CancelRequest(*http.Request)
|
||||
}
|
||||
switch rt := rt.(type) {
|
||||
case canceler:
|
||||
rt.CancelRequest(req)
|
||||
case utilnet.RoundTripperWrapper:
|
||||
TryCancelRequest(rt.WrappedRoundTripper(), req)
|
||||
default:
|
||||
klog.Warningf("Unable to cancel request for %T", rt)
|
||||
}
|
||||
}
|
||||
@@ -1,26 +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 utils
|
||||
|
||||
import "strings"
|
||||
|
||||
// Sanitize the inputs by removing line endings
|
||||
func Sanitize(s string) string {
|
||||
s = strings.ReplaceAll(s, "\n", "")
|
||||
s = strings.ReplaceAll(s, "\r", "")
|
||||
return s
|
||||
}
|
||||
@@ -1,28 +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 utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSanitize(t *testing.T) {
|
||||
s := "abc\ndef\rgh"
|
||||
require.Equal(t, "abcdefgh", Sanitize(s))
|
||||
}
|
||||
@@ -18,9 +18,8 @@ package schema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/kubevela/pkg/util/slices"
|
||||
)
|
||||
|
||||
// UISchema ui schema
|
||||
@@ -87,7 +86,7 @@ func (c Condition) Validate() error {
|
||||
if c.Action != "enable" && c.Action != "disable" && c.Action != "" {
|
||||
return fmt.Errorf("the action of the condition only supports enable, disable or leave it empty")
|
||||
}
|
||||
if c.Op != "" && !utils.StringsContain([]string{"==", "!=", "in"}, c.Op) {
|
||||
if c.Op != "" && !slices.Contains([]string{"==", "!=", "in"}, c.Op) {
|
||||
return fmt.Errorf("the op of the condition must be `==` 、`!=` and `in`")
|
||||
}
|
||||
return nil
|
||||
@@ -125,22 +124,6 @@ type Option struct {
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
// FirstUpper Sets the first letter of the string to upper.
|
||||
func FirstUpper(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
return strings.ToUpper(s[:1]) + s[1:]
|
||||
}
|
||||
|
||||
// FirstLower Sets the first letter of the string to lowercase.
|
||||
func FirstLower(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
return strings.ToLower(s[:1]) + s[1:]
|
||||
}
|
||||
|
||||
// GetDefaultUIType Set the default mapping for API Schema Type
|
||||
func GetDefaultUIType(apiType string, haveOptions bool, subType string, haveSub bool) string {
|
||||
switch apiType {
|
||||
@@ -170,15 +153,3 @@ func GetDefaultUIType(apiType string, haveOptions bool, subType string, haveSub
|
||||
return "Input"
|
||||
}
|
||||
}
|
||||
|
||||
// RenderLabel render option label
|
||||
func RenderLabel(source interface{}) string {
|
||||
switch v := source.(type) {
|
||||
case int:
|
||||
return fmt.Sprintf("%d", v)
|
||||
case string:
|
||||
return FirstUpper(v)
|
||||
default:
|
||||
return FirstUpper(fmt.Sprintf("%v", v))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,128 +17,9 @@ limitations under the License.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StringsContain strings contain
|
||||
func StringsContain(items []string, source string) bool {
|
||||
for _, item := range items {
|
||||
if item == source {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ThreeWaySliceCompare will compare two string slice, with the three return values: [both in A and B], [only in A], [only in B]
|
||||
func ThreeWaySliceCompare(a []string, b []string) ([]string, []string, []string) {
|
||||
m := make(map[string]struct{})
|
||||
for _, k := range b {
|
||||
m[k] = struct{}{}
|
||||
}
|
||||
|
||||
var AB, AO, BO []string
|
||||
for _, k := range a {
|
||||
_, ok := m[k]
|
||||
if !ok {
|
||||
AO = append(AO, k)
|
||||
continue
|
||||
}
|
||||
AB = append(AB, k)
|
||||
delete(m, k)
|
||||
}
|
||||
for k := range m {
|
||||
BO = append(BO, k)
|
||||
}
|
||||
sort.Strings(AB)
|
||||
sort.Strings(AO)
|
||||
sort.Strings(BO)
|
||||
return AB, AO, BO
|
||||
}
|
||||
|
||||
// EqualSlice checks if two slice are equal
|
||||
func EqualSlice(a, b []string) bool {
|
||||
sort.Strings(a)
|
||||
sort.Strings(b)
|
||||
return reflect.DeepEqual(a, b)
|
||||
}
|
||||
|
||||
// SliceIncludeSlice the slice include the b slice
|
||||
func SliceIncludeSlice(a, b []string) bool {
|
||||
if EqualSlice(a, b) {
|
||||
return true
|
||||
}
|
||||
for _, item := range b {
|
||||
if !StringsContain(a, item) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ToString convery an interface to a string type.
|
||||
func ToString(i interface{}) string {
|
||||
if i == nil {
|
||||
return ""
|
||||
}
|
||||
v := reflect.ValueOf(i)
|
||||
if v.Kind() == reflect.Ptr && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
i = v.Interface()
|
||||
switch s := i.(type) {
|
||||
case string:
|
||||
return s
|
||||
case bool:
|
||||
return strconv.FormatBool(s)
|
||||
case float64:
|
||||
return strconv.FormatFloat(s, 'f', -1, 64)
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(s), 'f', -1, 32)
|
||||
case int:
|
||||
return strconv.Itoa(s)
|
||||
case int64:
|
||||
return strconv.FormatInt(s, 10)
|
||||
case int32:
|
||||
return strconv.Itoa(int(s))
|
||||
case int16:
|
||||
return strconv.FormatInt(int64(s), 10)
|
||||
case int8:
|
||||
return strconv.FormatInt(int64(s), 10)
|
||||
case uint:
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case uint64:
|
||||
// nolint
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case uint32:
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case uint16:
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case uint8:
|
||||
return strconv.FormatUint(uint64(s), 10)
|
||||
case []byte:
|
||||
return string(s)
|
||||
case nil:
|
||||
return ""
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// MapKey2Array convery map keys to array
|
||||
func MapKey2Array(source map[string]string) []string {
|
||||
var list []string
|
||||
for k := range source {
|
||||
list = append(list, k)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// GetBoxDrawingString get line drawing string, see https://en.wikipedia.org/wiki/Box-drawing_character
|
||||
// nolint:gocyclo
|
||||
func GetBoxDrawingString(up bool, down bool, left bool, right bool, padLeft int, padRight int) string {
|
||||
@@ -193,14 +74,9 @@ func GetBoxDrawingString(up bool, down bool, left bool, right bool, padLeft int,
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// JoinURL join baseURL and subPath to be new URL
|
||||
func JoinURL(baseURL, subPath string) (string, error) {
|
||||
parsedURL, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
parsedURL.RawPath = path.Join(parsedURL.RawPath, subPath)
|
||||
parsedURL.Path = path.Join(parsedURL.Path, subPath)
|
||||
URL := parsedURL.String()
|
||||
return URL, nil
|
||||
// Sanitize the inputs by removing line endings
|
||||
func Sanitize(s string) string {
|
||||
s = strings.ReplaceAll(s, "\n", "")
|
||||
s = strings.ReplaceAll(s, "\r", "")
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -17,202 +17,12 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCompareSlice(t *testing.T) {
|
||||
caseA := []string{"c", "b", "a"}
|
||||
caseB := []string{"c", "b", "a"}
|
||||
gotab, gotao, gotbo := ThreeWaySliceCompare(caseA, caseB)
|
||||
assert.Equal(t, gotab, []string{"a", "b", "c"})
|
||||
assert.Equal(t, gotao, []string(nil))
|
||||
assert.Equal(t, gotbo, []string(nil))
|
||||
|
||||
caseA = []string{"c", "b"}
|
||||
caseB = []string{"c", "a"}
|
||||
gotab, gotao, gotbo = ThreeWaySliceCompare(caseA, caseB)
|
||||
assert.Equal(t, gotab, []string{"c"})
|
||||
assert.Equal(t, gotao, []string{"b"})
|
||||
assert.Equal(t, gotbo, []string{"a"})
|
||||
|
||||
caseA = []string{"c", "b"}
|
||||
caseB = nil
|
||||
gotab, gotao, gotbo = ThreeWaySliceCompare(caseA, caseB)
|
||||
assert.Equal(t, gotab, []string(nil))
|
||||
assert.Equal(t, gotao, []string{"b", "c"})
|
||||
assert.Equal(t, gotbo, []string(nil))
|
||||
|
||||
caseA = nil
|
||||
caseB = []string{"c", "b"}
|
||||
gotab, gotao, gotbo = ThreeWaySliceCompare(caseA, caseB)
|
||||
assert.Equal(t, gotab, []string(nil))
|
||||
assert.Equal(t, gotao, []string(nil))
|
||||
assert.Equal(t, gotbo, []string{"b", "c"})
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
caseA := []string{"c", "b", "a"}
|
||||
caseB := []string{"c", "b", "a"}
|
||||
assert.Equal(t, EqualSlice(caseA, caseB), true)
|
||||
|
||||
caseA = []string{"c", "a", "b"}
|
||||
caseB = []string{"c", "b", "a"}
|
||||
assert.Equal(t, EqualSlice(caseA, caseB), true)
|
||||
|
||||
caseA = []string{"c", "a", "b"}
|
||||
caseB = []string{"b", "a", "c"}
|
||||
assert.Equal(t, EqualSlice(caseA, caseB), true)
|
||||
|
||||
caseA = []string{"c", "a", "b"}
|
||||
caseB = []string{"b", "a", "c", "d"}
|
||||
assert.Equal(t, EqualSlice(caseA, caseB), false)
|
||||
|
||||
caseA = []string{}
|
||||
caseB = []string{}
|
||||
assert.Equal(t, EqualSlice(caseA, caseB), true)
|
||||
|
||||
caseA = []string{}
|
||||
caseB = []string{"b", "a", "c"}
|
||||
assert.Equal(t, EqualSlice(caseA, caseB), false)
|
||||
}
|
||||
|
||||
func TestSliceIncludeSlice(t *testing.T) {
|
||||
caseA := []string{"b", "a", "c"}
|
||||
caseB := []string{}
|
||||
assert.Equal(t, SliceIncludeSlice(caseA, caseB), true)
|
||||
|
||||
caseA = []string{"b", "a", "c"}
|
||||
caseB = []string{"b"}
|
||||
assert.Equal(t, SliceIncludeSlice(caseA, caseB), true)
|
||||
|
||||
caseA = []string{"b", "a", "c"}
|
||||
caseB = []string{"b", "c"}
|
||||
assert.Equal(t, SliceIncludeSlice(caseA, caseB), true)
|
||||
|
||||
caseA = []string{"b", "a", "c"}
|
||||
caseB = []string{"b", "c", "d"}
|
||||
assert.Equal(t, SliceIncludeSlice(caseA, caseB), false)
|
||||
|
||||
caseA = []string{"b", "a", "c"}
|
||||
caseB = []string{"b", "c", "a"}
|
||||
assert.Equal(t, SliceIncludeSlice(caseA, caseB), true)
|
||||
}
|
||||
|
||||
func TestJoinURL(t *testing.T) {
|
||||
|
||||
testcase := []struct {
|
||||
baseURL string
|
||||
subPath string
|
||||
expectedUrl string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
baseURL: "https://www.kubevela.com",
|
||||
subPath: "index.yaml",
|
||||
expectedUrl: "https://www.kubevela.com/index.yaml",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
baseURL: "http://www.kubevela.com",
|
||||
subPath: "index.yaml",
|
||||
expectedUrl: "http://www.kubevela.com/index.yaml",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
baseURL: "0x7f:",
|
||||
subPath: "index.yaml",
|
||||
expectedUrl: "",
|
||||
err: &url.Error{Op: "parse", URL: "0x7f:", Err: errors.New("first path segment in URL cannot contain colon")},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcase {
|
||||
url, err := JoinURL(tc.baseURL, tc.subPath)
|
||||
assert.Equal(t, tc.expectedUrl, url)
|
||||
assert.Equal(t, tc.err, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestToString(t *testing.T) {
|
||||
type Obj struct {
|
||||
k string
|
||||
}
|
||||
obj := &Obj{"foo"}
|
||||
boolPtr := true
|
||||
caseA := []struct {
|
||||
input interface{}
|
||||
expect string
|
||||
}{
|
||||
{int(666), "666"},
|
||||
{int8(6), "6"},
|
||||
{int16(6), "6"},
|
||||
{int32(6), "6"},
|
||||
{int64(6), "6"},
|
||||
{uint(6), "6"},
|
||||
{uint8(6), "6"},
|
||||
{uint16(6), "6"},
|
||||
{uint32(6), "6"},
|
||||
{uint64(6), "6"},
|
||||
{float32(3.14), "3.14"},
|
||||
{float64(3.14), "3.14"},
|
||||
{true, "true"},
|
||||
{false, "false"},
|
||||
{&boolPtr, "true"},
|
||||
{nil, ""},
|
||||
{[]byte("one time"), "one time"},
|
||||
{"one more time", "one more time"},
|
||||
{obj, ""},
|
||||
}
|
||||
|
||||
for _, test := range caseA {
|
||||
v := ToString(test.input)
|
||||
assert.Equal(t, test.expect, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringsContain(t *testing.T) {
|
||||
type args struct {
|
||||
items []string
|
||||
source string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "test empty items should not contain any string",
|
||||
args: args{
|
||||
items: []string{},
|
||||
source: "foo",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "test items should contain source",
|
||||
args: args{
|
||||
items: []string{"foo", "bar"},
|
||||
source: "foo",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "test items should not contain source",
|
||||
args: args{
|
||||
items: []string{"foo", "bar"},
|
||||
source: "baz",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, StringsContain(tt.args.items, tt.args.source), "StringsContain(%v, %v)", tt.args.items, tt.args.source)
|
||||
})
|
||||
}
|
||||
func TestSanitize(t *testing.T) {
|
||||
s := "abc\ndef\rgh"
|
||||
require.Equal(t, "abcdefgh", Sanitize(s))
|
||||
}
|
||||
|
||||
@@ -54,15 +54,6 @@ func GetVelaHomeDir() (string, error) {
|
||||
return velaHome, nil
|
||||
}
|
||||
|
||||
// GetDefaultFrontendDir return default vela frontend dir
|
||||
func GetDefaultFrontendDir() (string, error) {
|
||||
home, err := GetVelaHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(home, "frontend"), nil
|
||||
}
|
||||
|
||||
// GetCapCenterDir return cap center dir
|
||||
func GetCapCenterDir() (string, error) {
|
||||
home, err := GetVelaHomeDir()
|
||||
|
||||
@@ -19,6 +19,7 @@ package utils
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
@@ -70,3 +71,15 @@ func IsValidURL(strURL string) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// JoinURL join baseURL and subPath to be new URL
|
||||
func JoinURL(baseURL, subPath string) (string, error) {
|
||||
parsedURL, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
parsedURL.RawPath = path.Join(parsedURL.RawPath, subPath)
|
||||
parsedURL.Path = path.Join(parsedURL.Path, subPath)
|
||||
URL := parsedURL.String()
|
||||
return URL, nil
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ limitations under the License.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -113,3 +115,37 @@ func TestIsValidURL(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinURL(t *testing.T) {
|
||||
testcase := []struct {
|
||||
baseURL string
|
||||
subPath string
|
||||
expectedUrl string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
baseURL: "https://www.kubevela.com",
|
||||
subPath: "index.yaml",
|
||||
expectedUrl: "https://www.kubevela.com/index.yaml",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
baseURL: "http://www.kubevela.com",
|
||||
subPath: "index.yaml",
|
||||
expectedUrl: "http://www.kubevela.com/index.yaml",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
baseURL: "0x7f:",
|
||||
subPath: "index.yaml",
|
||||
expectedUrl: "",
|
||||
err: &url.Error{Op: "parse", URL: "0x7f:", Err: fmt.Errorf("first path segment in URL cannot contain colon")},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcase {
|
||||
url, err := JoinURL(tc.baseURL, tc.subPath)
|
||||
assert.Equal(t, tc.expectedUrl, url)
|
||||
assert.Equal(t, tc.err, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kubevela/pkg/util/slices"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/networking/v1"
|
||||
networkv1beta1 "k8s.io/api/networking/v1beta1"
|
||||
@@ -41,7 +42,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
apis "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
querytypes "github.com/oam-dev/kubevela/pkg/velaql/providers/query/types"
|
||||
)
|
||||
|
||||
@@ -236,7 +236,7 @@ func generatorFromIngress(ingress v1.Ingress, cluster, component string) (servic
|
||||
getAppProtocol := func(host string) string {
|
||||
if len(ingress.Spec.TLS) > 0 {
|
||||
for _, tls := range ingress.Spec.TLS {
|
||||
if len(tls.Hosts) > 0 && utils.StringsContain(tls.Hosts, host) {
|
||||
if len(tls.Hosts) > 0 && slices.Contains(tls.Hosts, host) {
|
||||
return querytypes.HTTPS
|
||||
}
|
||||
if len(tls.Hosts) == 0 {
|
||||
|
||||
@@ -169,25 +169,5 @@ var _ = Describe("Test ComponentDefinition validating handler", func() {
|
||||
Expect(resp.Allowed).Should(BeFalse())
|
||||
Expect(resp.Result.Reason).Should(Equal(metav1.StatusReason("the type and the definition of the workload field in ComponentDefinition wrongCd should represent the same workload")))
|
||||
})
|
||||
|
||||
It("Test HELM type componentDefinition without definition", func() {
|
||||
helmCd := v1beta1.ComponentDefinition{}
|
||||
helmCd.SetGroupVersionKind(v1beta1.ComponentDefinitionGroupVersionKind)
|
||||
helmCd.SetName("helmCd")
|
||||
helmCd.Spec.Workload.Type = "deployments.apps"
|
||||
helmCd.Spec.Schematic = &common.Schematic{
|
||||
HELM: &common.Helm{},
|
||||
}
|
||||
helmCdRaw, _ := json.Marshal(helmCd)
|
||||
req := admission.Request{
|
||||
AdmissionRequest: admissionv1.AdmissionRequest{
|
||||
Operation: admissionv1.Create,
|
||||
Resource: reqResource,
|
||||
Object: runtime.RawExtension{Raw: helmCdRaw},
|
||||
},
|
||||
}
|
||||
resp := handler.Handle(context.TODO(), req)
|
||||
Expect(resp.Allowed).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -40,6 +40,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/resourcetracker"
|
||||
"github.com/oam-dev/kubevela/pkg/rollout"
|
||||
kubevelaapp "github.com/oam-dev/kubevela/pkg/utils/app"
|
||||
errors3 "github.com/oam-dev/kubevela/pkg/utils/errors"
|
||||
)
|
||||
|
||||
@@ -320,7 +321,7 @@ func (wo appWorkflowOperator) Rollback(ctx context.Context) error {
|
||||
}
|
||||
appKey := client.ObjectKeyFromObject(app)
|
||||
// rollback application spec and freeze
|
||||
controllerRequirement, err := utils.FreezeApplication(ctx, wo.cli, app, func() {
|
||||
controllerRequirement, err := kubevelaapp.FreezeApplication(ctx, wo.cli, app, func() {
|
||||
app.Spec = rev.Spec.Application.Spec
|
||||
oam.SetPublishVersion(app, publishVersion)
|
||||
})
|
||||
@@ -369,7 +370,7 @@ func (wo appWorkflowOperator) Rollback(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// unfreeze application
|
||||
if err = utils.UnfreezeApplication(ctx, wo.cli, app, nil, controllerRequirement); err != nil {
|
||||
if err = kubevelaapp.UnfreezeApplication(ctx, wo.cli, app, nil, controllerRequirement); err != nil {
|
||||
return errors.Wrapf(err, "failed to resume application to restart")
|
||||
}
|
||||
|
||||
|
||||
@@ -48,27 +48,6 @@ type provider struct {
|
||||
renderer oamProvider.WorkloadRenderer
|
||||
}
|
||||
|
||||
// ReadPlacementDecisions
|
||||
// Deprecated
|
||||
func (p *provider) ReadPlacementDecisions(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act wfTypes.Action) error {
|
||||
policy, err := v.GetString("inputs", "policyName")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env, err := v.GetString("inputs", "envName")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decisions, exists, err := envbinding.ReadPlacementDecisions(p.app, policy, env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return v.FillObject(map[string]interface{}{"decisions": decisions}, "outputs")
|
||||
}
|
||||
return v.FillObject(map[string]interface{}{}, "outputs")
|
||||
}
|
||||
|
||||
// MakePlacementDecisions
|
||||
// Deprecated
|
||||
func (p *provider) MakePlacementDecisions(ctx monitorContext.Context, wfCtx wfContext.Context, v *value.Value, act wfTypes.Action) error {
|
||||
@@ -210,7 +189,6 @@ func (p *provider) GetPlacementsFromTopologyPolicies(ctx monitorContext.Context,
|
||||
func Install(p wfTypes.Providers, c client.Client, app *v1beta1.Application, af *appfile.Appfile, apply oamProvider.ComponentApply, healthCheck oamProvider.ComponentHealthCheck, renderer oamProvider.WorkloadRenderer) {
|
||||
prd := &provider{Client: c, app: app, af: af, apply: apply, healthCheck: healthCheck, renderer: renderer}
|
||||
p.Register(ProviderName, map[string]wfTypes.Handler{
|
||||
"read-placement-decisions": prd.ReadPlacementDecisions,
|
||||
"make-placement-decisions": prd.MakePlacementDecisions,
|
||||
"patch-application": prd.PatchApplication,
|
||||
"list-clusters": prd.ListClusters,
|
||||
|
||||
@@ -40,93 +40,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
)
|
||||
|
||||
func TestReadPlacementDecisions(t *testing.T) {
|
||||
testCases := []struct {
|
||||
InputVal map[string]interface{}
|
||||
OldCluster string
|
||||
OldNamespace string
|
||||
ExpectError string
|
||||
ExpectDecisionExists bool
|
||||
ExpectCluster string
|
||||
ExpectNamespace string
|
||||
}{{
|
||||
InputVal: map[string]interface{}{},
|
||||
ExpectError: "var(path=inputs.policyName) not exist",
|
||||
}, {
|
||||
InputVal: map[string]interface{}{
|
||||
"policyName": "example-policy",
|
||||
},
|
||||
ExpectError: "var(path=inputs.envName) not exist",
|
||||
}, {
|
||||
InputVal: map[string]interface{}{
|
||||
"policyName": "example-policy",
|
||||
"envName": "example-env",
|
||||
},
|
||||
ExpectError: "",
|
||||
ExpectDecisionExists: false,
|
||||
}, {
|
||||
InputVal: map[string]interface{}{
|
||||
"policyName": "example-policy",
|
||||
"envName": "example-env",
|
||||
},
|
||||
OldCluster: "example-cluster",
|
||||
OldNamespace: "example-namespace",
|
||||
ExpectError: "",
|
||||
ExpectDecisionExists: true,
|
||||
ExpectCluster: "example-cluster",
|
||||
ExpectNamespace: "example-namespace",
|
||||
}}
|
||||
r := require.New(t)
|
||||
for _, testCase := range testCases {
|
||||
cli := fake.NewClientBuilder().WithScheme(common.Scheme).Build()
|
||||
app := &v1beta1.Application{}
|
||||
p := &provider{
|
||||
Client: cli,
|
||||
app: app,
|
||||
}
|
||||
act := &mock.Action{}
|
||||
v, err := value.NewValue("", nil, "")
|
||||
r.NoError(err)
|
||||
r.NoError(v.FillObject(testCase.InputVal, "inputs"))
|
||||
if testCase.ExpectCluster != "" || testCase.ExpectNamespace != "" {
|
||||
pd := v1alpha1.PlacementDecision{
|
||||
Cluster: testCase.OldCluster,
|
||||
Namespace: testCase.OldNamespace,
|
||||
}
|
||||
bs, err := json.Marshal(&v1alpha1.EnvBindingStatus{
|
||||
Envs: []v1alpha1.EnvStatus{{
|
||||
Env: "example-env",
|
||||
Placements: []v1alpha1.PlacementDecision{pd},
|
||||
}},
|
||||
})
|
||||
r.NoError(err)
|
||||
app.Status.PolicyStatus = []apicommon.PolicyStatus{{
|
||||
Name: "example-policy",
|
||||
Type: v1alpha1.EnvBindingPolicyType,
|
||||
Status: &runtime.RawExtension{Raw: bs},
|
||||
}}
|
||||
}
|
||||
err = p.ReadPlacementDecisions(nil, nil, v, act)
|
||||
if testCase.ExpectError == "" {
|
||||
r.NoError(err)
|
||||
} else {
|
||||
r.Contains(err.Error(), testCase.ExpectError)
|
||||
continue
|
||||
}
|
||||
outputs, err := v.LookupValue("outputs")
|
||||
r.NoError(err)
|
||||
md := map[string][]v1alpha1.PlacementDecision{}
|
||||
r.NoError(outputs.UnmarshalTo(&md))
|
||||
if !testCase.ExpectDecisionExists {
|
||||
r.Equal(0, len(md))
|
||||
} else {
|
||||
r.Equal(1, len(md["decisions"]))
|
||||
r.Equal(testCase.ExpectCluster, md["decisions"][0].Cluster)
|
||||
r.Equal(testCase.ExpectNamespace, md["decisions"][0].Namespace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakePlacementDecisions(t *testing.T) {
|
||||
multicluster.ClusterGatewaySecretNamespace = types.DefaultKubeVelaNS
|
||||
testCases := []struct {
|
||||
|
||||
@@ -1,100 +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 apis
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
)
|
||||
|
||||
// Environment contains all info needed in `vela env` command
|
||||
type Environment struct {
|
||||
EnvName string `json:"envName" binding:"required,min=1,max=32"`
|
||||
Namespace string `json:"namespace" binding:"required,min=1,max=32"`
|
||||
Email string `json:"email"`
|
||||
Domain string `json:"domain"`
|
||||
Current string `json:"current,omitempty"`
|
||||
}
|
||||
|
||||
// EnvironmentBody used for restful API in dashboard server
|
||||
type EnvironmentBody struct {
|
||||
Namespace string `json:"namespace" binding:"required,min=1,max=32"`
|
||||
}
|
||||
|
||||
// Response used for restful API response in dashboard server
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Data interface{} `json:"data" swaggerignore:"true"`
|
||||
}
|
||||
|
||||
// CommonFlag used for restful API flags in dashboard server
|
||||
type CommonFlag struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// WorkloadMeta store workload metadata for dashboard restful API server
|
||||
type WorkloadMeta struct {
|
||||
Name string `json:"name"`
|
||||
Parameters []types.Parameter `json:"parameters,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// TraitBody used to present trait which is to be attached and, of which parameters are set
|
||||
type TraitBody struct {
|
||||
EnvName string `json:"envName"`
|
||||
Name string `json:"name"`
|
||||
Flags []CommonFlag `json:"flags"`
|
||||
ComponentName string `json:"componentName"`
|
||||
AppName string `json:"appName,omitempty"`
|
||||
Staging string `json:"staging,omitempty"`
|
||||
}
|
||||
|
||||
// ComponentMeta store component info for dashboard restful API server
|
||||
type ComponentMeta struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Workload runtime.RawExtension `json:"workload,omitempty"`
|
||||
// WorkloadName for `vela comp ls`
|
||||
WorkloadName string `json:"workloadName,omitempty"`
|
||||
// TraitNames for `vela comp ls`
|
||||
TraitNames []string `json:"traitsNames,omitempty"`
|
||||
App string `json:"app"`
|
||||
CreatedTime string `json:"createdTime,omitempty"`
|
||||
}
|
||||
|
||||
// ApplicationMeta used for dashboard restful API server
|
||||
type ApplicationMeta struct {
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Components []ComponentMeta `json:"components,omitempty"`
|
||||
CreatedTime string `json:"createdTime,omitempty"`
|
||||
}
|
||||
|
||||
// CapabilityMeta used for dashboard restful API server
|
||||
type CapabilityMeta struct {
|
||||
CapabilityName string `json:"capabilityName"`
|
||||
CapabilityCenterName string `json:"capabilityCenterName,omitempty"`
|
||||
}
|
||||
|
||||
// RegistryConfig is used to store registry config in file
|
||||
type RegistryConfig struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
@@ -17,14 +17,8 @@ limitations under the License.
|
||||
package appfile
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/references/appfile/api"
|
||||
)
|
||||
|
||||
@@ -47,73 +41,3 @@ func SetWorkload(app *api.Application, componentName, workloadType string, workl
|
||||
app.Services[componentName] = s
|
||||
return Validate(app)
|
||||
}
|
||||
|
||||
// SetTrait will set user trait for Appfile
|
||||
func SetTrait(app *v1beta1.Application, componentName, traitType string, traitData map[string]interface{}) error {
|
||||
if app == nil {
|
||||
return errorAppNilPointer
|
||||
}
|
||||
if traitData == nil {
|
||||
traitData = make(map[string]interface{})
|
||||
}
|
||||
data, err := json.Marshal(traitData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fail to marshal trait data %w", err)
|
||||
}
|
||||
var foundComp bool
|
||||
for idx, comp := range app.Spec.Components {
|
||||
if comp.Name != componentName {
|
||||
continue
|
||||
}
|
||||
foundComp = true
|
||||
var added bool
|
||||
for j, tr := range app.Spec.Components[idx].Traits {
|
||||
if tr.Type != traitType {
|
||||
continue
|
||||
}
|
||||
added = true
|
||||
if app.Spec.Components[idx].Traits[j].Properties == nil && data != nil {
|
||||
app.Spec.Components[idx].Traits[j].Properties = &runtime.RawExtension{Raw: data}
|
||||
} else {
|
||||
app.Spec.Components[idx].Traits[j].Properties.Raw = data
|
||||
}
|
||||
}
|
||||
if !added {
|
||||
app.Spec.Components[idx].Traits = append(app.Spec.Components[idx].Traits, common.ApplicationTrait{Type: traitType, Properties: &runtime.RawExtension{Raw: data}})
|
||||
}
|
||||
}
|
||||
if !foundComp {
|
||||
return errors.New(componentName + " not found in app " + app.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveTrait will remove a trait from Appfile
|
||||
func RemoveTrait(app *api.Application, componentName, traitType string) error {
|
||||
if app == nil {
|
||||
return errorAppNilPointer
|
||||
}
|
||||
|
||||
s, ok := app.Services[componentName]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
delete(s, traitType)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveComponent will remove component from Application
|
||||
func RemoveComponent(app *v1beta1.Application, componentName string) error {
|
||||
if app == nil {
|
||||
return errorAppNilPointer
|
||||
}
|
||||
var newComps []common.ApplicationComponent
|
||||
for _, comp := range app.Spec.Components {
|
||||
if comp.Name == componentName {
|
||||
continue
|
||||
}
|
||||
newComps = append(newComps, comp)
|
||||
}
|
||||
app.Spec.Components = newComps
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -41,10 +41,16 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/system"
|
||||
cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
"github.com/oam-dev/kubevela/references/apis"
|
||||
"github.com/oam-dev/kubevela/references/docgen"
|
||||
)
|
||||
|
||||
// RegistryConfig is used to store registry config in file
|
||||
type RegistryConfig struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// NewRegistryCommand Manage Capability Center
|
||||
func NewRegistryCommand(ioStream cmdutil.IOStreams, order string) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
@@ -150,7 +156,7 @@ func listCapRegistrys(ioStreams cmdutil.IOStreams) error {
|
||||
|
||||
// addRegistry will add a registry
|
||||
func addRegistry(regName, regURL, regToken string) error {
|
||||
regConfig := apis.RegistryConfig{
|
||||
regConfig := RegistryConfig{
|
||||
Name: regName, URL: regURL, Token: regToken,
|
||||
}
|
||||
repos, err := ListRegistryConfig()
|
||||
@@ -222,7 +228,7 @@ type GithubRegistry struct {
|
||||
}
|
||||
|
||||
// NewRegistryFromConfig return Registry interface to get capabilities
|
||||
func NewRegistryFromConfig(config apis.RegistryConfig) (Registry, error) {
|
||||
func NewRegistryFromConfig(config RegistryConfig) (Registry, error) {
|
||||
return NewRegistry(context.TODO(), config.Token, config.Name, config.URL)
|
||||
}
|
||||
|
||||
@@ -273,9 +279,8 @@ func NewRegistry(ctx context.Context, token, registryName string, regURL string)
|
||||
|
||||
// ListRegistryConfig will get all registry config stored in local
|
||||
// this will return at least one config, which is DefaultRegistry
|
||||
func ListRegistryConfig() ([]apis.RegistryConfig, error) {
|
||||
|
||||
defaultRegistryConfig := apis.RegistryConfig{Name: DefaultRegistry, URL: "oss://registry.kubevela.net/"}
|
||||
func ListRegistryConfig() ([]RegistryConfig, error) {
|
||||
defaultRegistryConfig := RegistryConfig{Name: DefaultRegistry, URL: "oss://registry.kubevela.net/"}
|
||||
config, err := system.GetRepoConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -283,7 +288,7 @@ func ListRegistryConfig() ([]apis.RegistryConfig, error) {
|
||||
data, err := os.ReadFile(filepath.Clean(config))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err := StoreRepos([]apis.RegistryConfig{defaultRegistryConfig})
|
||||
err := StoreRepos([]RegistryConfig{defaultRegistryConfig})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error initialize default registry")
|
||||
}
|
||||
@@ -291,7 +296,7 @@ func ListRegistryConfig() ([]apis.RegistryConfig, error) {
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var regConfigs []apis.RegistryConfig
|
||||
var regConfigs []RegistryConfig
|
||||
if err = yaml.Unmarshal(data, ®Configs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -722,7 +727,7 @@ func Parse(addr string) (string, *Content, error) {
|
||||
}
|
||||
|
||||
// StoreRepos will store registry repo locally
|
||||
func StoreRepos(registries []apis.RegistryConfig) error {
|
||||
func StoreRepos(registries []RegistryConfig) error {
|
||||
config, err := system.GetRepoConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,21 +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 fake
|
||||
|
||||
// FrontendSource is a base64-encoded, gzipped tarball of the default Frontend Static files.
|
||||
// This whole file will be rewrite at build time.
|
||||
var FrontendSource string
|
||||
@@ -1,17 +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 common
|
||||
@@ -1,59 +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 common
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
|
||||
monitoring "github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// InstallPrometheusInstance will install prometheus instance when the Capability is 'metrics'
|
||||
func InstallPrometheusInstance(kubecli client.Client) error {
|
||||
var promIns = monitoring.Prometheus{}
|
||||
err := kubecli.Get(context.Background(), types.NamespacedName{Namespace: "monitoring", Name: "oam"}, &promIns)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
promIns.Name = "oam"
|
||||
promIns.Namespace = "monitoring"
|
||||
promIns.SetLabels(map[string]string{"prometheus": "kubevela"})
|
||||
promIns.Spec = monitoring.PrometheusSpec{
|
||||
Image: pointer.String("quay.io/prometheus/prometheus:v2.19.2"),
|
||||
NodeSelector: map[string]string{"kubernetes.io/os": "linux"},
|
||||
Replicas: pointer.Int32(1),
|
||||
ServiceAccountName: "kube-prometheus-stack-prometheus",
|
||||
SecurityContext: &corev1.PodSecurityContext{
|
||||
RunAsUser: pointer.Int64(1000),
|
||||
RunAsNonRoot: pointer.Bool(true),
|
||||
FSGroup: pointer.Int64(2000),
|
||||
},
|
||||
ServiceMonitorSelector: &v1.LabelSelector{MatchLabels: map[string]string{"k8s-app": "oam", "controller": "metricsTrait"}},
|
||||
ServiceMonitorNamespaceSelector: &v1.LabelSelector{
|
||||
MatchLabels: util.OAMLabel,
|
||||
},
|
||||
Version: "v2.19.2",
|
||||
}
|
||||
return kubecli.Create(context.Background(), &promIns)
|
||||
}
|
||||
@@ -378,12 +378,6 @@ func HandleTemplate(in *runtime.RawExtension, schematic *commontypes.Schematic,
|
||||
tmp.Path = schematic.Terraform.Path
|
||||
return tmp, nil
|
||||
}
|
||||
if schematic.KUBE != nil {
|
||||
tmp.Category = types.KubeCategory
|
||||
tmp.KubeTemplate = schematic.KUBE.Template
|
||||
tmp.KubeParameter = schematic.KUBE.Parameters
|
||||
return tmp, nil
|
||||
}
|
||||
}
|
||||
if tmp.CueTemplateURI != "" {
|
||||
b, err := common.HTTPGetWithOption(context.Background(), tmp.CueTemplateURI, nil)
|
||||
@@ -393,10 +387,6 @@ func HandleTemplate(in *runtime.RawExtension, schematic *commontypes.Schematic,
|
||||
tmp.CueTemplate = string(b)
|
||||
}
|
||||
if tmp.CueTemplate == "" {
|
||||
if schematic != nil && schematic.HELM != nil {
|
||||
tmp.Category = types.HelmCategory
|
||||
return tmp, nil
|
||||
}
|
||||
return types.Capability{}, errors.New("template not exist in definition")
|
||||
}
|
||||
tmp.Parameters, err = cue.GetParameters(tmp.CueTemplate, pd)
|
||||
|
||||
@@ -18,7 +18,6 @@ package docgen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -155,18 +154,8 @@ var _ = Describe("test GetCapabilityByName", func() {
|
||||
defaultNS string
|
||||
cd1 corev1beta1.ComponentDefinition
|
||||
cd2 corev1beta1.ComponentDefinition
|
||||
cd3 corev1beta1.ComponentDefinition
|
||||
cd4 corev1beta1.ComponentDefinition
|
||||
td1 corev1beta1.TraitDefinition
|
||||
td2 corev1beta1.TraitDefinition
|
||||
td3 corev1beta1.TraitDefinition
|
||||
component1 string
|
||||
component2 string
|
||||
component3 string
|
||||
component4 string
|
||||
trait1 string
|
||||
trait2 string
|
||||
trait3 string
|
||||
)
|
||||
BeforeEach(func() {
|
||||
c = common.Args{}
|
||||
@@ -176,12 +165,6 @@ var _ = Describe("test GetCapabilityByName", func() {
|
||||
defaultNS = types.DefaultKubeVelaNS
|
||||
component1 = "cd1"
|
||||
component2 = "cd2"
|
||||
component3 = "cd3"
|
||||
component4 = "cd4"
|
||||
|
||||
trait1 = "td1"
|
||||
trait2 = "td2"
|
||||
trait3 = "td3"
|
||||
|
||||
By("create namespace")
|
||||
Expect(k8sClient.Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}})).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
@@ -191,11 +174,6 @@ var _ = Describe("test GetCapabilityByName", func() {
|
||||
data, _ := os.ReadFile("testdata/componentDef.yaml")
|
||||
yaml.Unmarshal(data, &cd1)
|
||||
yaml.Unmarshal(data, &cd2)
|
||||
data2, _ := os.ReadFile("testdata/kube-worker.yaml")
|
||||
yaml.Unmarshal(data2, &cd3)
|
||||
|
||||
helmYaml, _ := os.ReadFile("testdata/helm.yaml")
|
||||
yaml.Unmarshal(helmYaml, &cd4)
|
||||
|
||||
cd1.Namespace = ns
|
||||
cd1.Name = component1
|
||||
@@ -204,38 +182,10 @@ var _ = Describe("test GetCapabilityByName", func() {
|
||||
cd2.Namespace = defaultNS
|
||||
cd2.Name = component2
|
||||
Expect(k8sClient.Create(ctx, &cd2)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
cd3.Namespace = ns
|
||||
cd3.Name = component3
|
||||
Expect(k8sClient.Create(ctx, &cd3)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
cd4.Namespace = ns
|
||||
cd4.Name = component4
|
||||
Expect(k8sClient.Create(ctx, &cd4)).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
By("create TraitDefinition")
|
||||
data3, _ := os.ReadFile("testdata/svcTraitDef.yaml")
|
||||
yaml.Unmarshal(data3, &td3)
|
||||
|
||||
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{}))
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
for _, obj := range []client.Object{&cd1, &cd2, &cd3, &cd4, &td1, &td2, &td3} {
|
||||
for _, obj := range []client.Object{&cd1, &cd2} {
|
||||
key := client.ObjectKeyFromObject(obj)
|
||||
Expect(k8sClient.Delete(ctx, obj)).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
@@ -253,36 +203,6 @@ var _ = Describe("test GetCapabilityByName", func() {
|
||||
_, err = GetCapabilityByName(ctx, c, component2, ns, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
By("ComponentDefinition is in the default namespace")
|
||||
cap, err := GetCapabilityByName(ctx, c, component3, ns, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
jsontmp, err := json.Marshal(cap.KubeParameter)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(string(jsontmp)).Should(ContainSubstring("image"))
|
||||
Expect(string(jsontmp)).Should(ContainSubstring("spec.template.spec.containers[0].image"))
|
||||
Expect(string(jsontmp)).Should(ContainSubstring("port"))
|
||||
Expect(string(jsontmp)).Should(ContainSubstring("the specific container port num which can accept external request."))
|
||||
|
||||
By("ComponentDefinition's workload type is AutoDetectWorkloadDefinition")
|
||||
_, err = GetCapabilityByName(ctx, c, component4, ns, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
By("TraitDefinition is in the current namespace")
|
||||
_, err = GetCapabilityByName(ctx, c, trait1, ns, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
By("TraitDefinition is in the default namespace")
|
||||
_, err = GetCapabilityByName(ctx, c, trait2, ns, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
By("TraitDefinition is in the default namespace")
|
||||
cap, err = GetCapabilityByName(ctx, c, trait3, ns, nil)
|
||||
Expect(err).Should(BeNil())
|
||||
jsontmp, err = json.Marshal(cap.KubeParameter)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(string(jsontmp)).Should(ContainSubstring("targetPort"))
|
||||
Expect(string(jsontmp)).Should(ContainSubstring("target port num for service provider."))
|
||||
|
||||
By("capability cloud not be found")
|
||||
_, err = GetCapabilityByName(ctx, c, "a-component-definition-not-existed", ns, nil)
|
||||
Expect(err).Should(HaveOccurred())
|
||||
|
||||
@@ -150,11 +150,6 @@ func (ref *ConsoleReference) Show(ctx context.Context, c common.Args, ioStreams
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case types.HelmCategory, types.KubeCategory:
|
||||
_, propertyConsole, err = ref.GenerateHelmAndKubeProperties(ctx, capability)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupport capability category %s", capability.Category)
|
||||
}
|
||||
|
||||
@@ -176,14 +176,6 @@ func (ref *MarkdownReference) GenerateMarkdownForCap(ctx context.Context, c type
|
||||
klog.Warningf("failed to get base resource kinds for %s: %v", c.Name, warnErr)
|
||||
}
|
||||
}
|
||||
case types.HelmCategory, types.KubeCategory:
|
||||
properties, _, err := ref.GenerateHelmAndKubeProperties(ctx, &c)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to retrieve `parameters` value from %s with err: %w", c.Name, err)
|
||||
}
|
||||
for _, property := range properties {
|
||||
generatedDoc += ref.getParameterString("###"+property.Name, property.Parameters, types.HelmCategory)
|
||||
}
|
||||
case types.TerraformCategory:
|
||||
generatedDoc, err = ref.GenerateTerraformCapabilityPropertiesAndOutputs(c)
|
||||
if err != nil {
|
||||
@@ -319,16 +311,6 @@ func (ref *MarkdownReference) getParameterString(tableName string, parameterList
|
||||
tab += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, ref.prettySentence(p.Usage), ref.formatTableString(p.PrintableType), p.Required, printableDefaultValue)
|
||||
}
|
||||
}
|
||||
case types.HelmCategory:
|
||||
for _, p := range parameterList {
|
||||
printableDefaultValue := ref.getJSONPrintableDefaultValue(p.JSONType, p.Default)
|
||||
tab += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, ref.prettySentence(p.Usage), ref.formatTableString(p.PrintableType), p.Required, printableDefaultValue)
|
||||
}
|
||||
case types.KubeCategory:
|
||||
for _, p := range parameterList {
|
||||
// Kube parameter doesn't have default value
|
||||
tab += fmt.Sprintf(" %s | %s | %s | %t | %s \n", p.Name, ref.prettySentence(p.Usage), ref.formatTableString(p.PrintableType), p.Required, "")
|
||||
}
|
||||
case types.TerraformCategory:
|
||||
// Terraform doesn't have default value
|
||||
for _, p := range parameterList {
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rogpeppe/go-internal/modfile"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@@ -49,7 +48,6 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/controller/utils"
|
||||
velacue "github.com/oam-dev/kubevela/pkg/cue"
|
||||
velaprocess "github.com/oam-dev/kubevela/pkg/cue/process"
|
||||
pkgdef "github.com/oam-dev/kubevela/pkg/definition"
|
||||
pkgUtils "github.com/oam-dev/kubevela/pkg/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
@@ -148,11 +146,6 @@ func (ref *ParseReference) prepareConsoleParameter(tableName string, parameterLi
|
||||
table.Append([]string{ref.I18N.Get(p.Name), ref.prettySentence(p.Usage), ref.I18N.Get(p.PrintableType), ref.I18N.Get(strconv.FormatBool(p.Required)), ref.I18N.Get(printableDefaultValue)})
|
||||
}
|
||||
}
|
||||
case types.HelmCategory, types.KubeCategory:
|
||||
for _, p := range parameterList {
|
||||
printableDefaultValue := ref.getJSONPrintableDefaultValue(p.JSONType, p.Default)
|
||||
table.Append([]string{ref.I18N.Get(p.Name), ref.prettySentence(p.Usage), ref.I18N.Get(p.PrintableType), ref.I18N.Get(strconv.FormatBool(p.Required)), ref.I18N.Get(printableDefaultValue)})
|
||||
}
|
||||
case types.TerraformCategory:
|
||||
// Terraform doesn't have default value
|
||||
for _, p := range parameterList {
|
||||
@@ -432,20 +425,6 @@ func (ref *ParseReference) getCUEPrintableDefaultValue(v interface{}) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (ref *ParseReference) getJSONPrintableDefaultValue(dataType string, value interface{}) string {
|
||||
if value != nil {
|
||||
return strings.TrimSpace(fmt.Sprintf("%v", value))
|
||||
}
|
||||
defaultValueMap := map[string]string{
|
||||
"number": "0",
|
||||
"boolean": "false",
|
||||
"string": "\"\"",
|
||||
"object": "{}",
|
||||
"array": "[]",
|
||||
}
|
||||
return defaultValueMap[dataType]
|
||||
}
|
||||
|
||||
// CommonReference contains parameters info of HelmCategory and KubuCategory type capability at present
|
||||
type CommonReference struct {
|
||||
Name string
|
||||
@@ -459,40 +438,6 @@ type CommonSchema struct {
|
||||
Schemas *openapi3.Schema
|
||||
}
|
||||
|
||||
// GenerateHelmAndKubeProperties get all properties of a Helm/Kube Category type capability
|
||||
func (ref *ParseReference) GenerateHelmAndKubeProperties(ctx context.Context, capability *types.Capability) ([]CommonReference, []ConsoleReference, error) {
|
||||
cmName := fmt.Sprintf("%s%s", types.CapabilityConfigMapNamePrefix, capability.Name)
|
||||
switch capability.Type {
|
||||
case types.TypeComponentDefinition:
|
||||
cmName = fmt.Sprintf("component-%s", cmName)
|
||||
case types.TypeTrait:
|
||||
cmName = fmt.Sprintf("trait-%s", cmName)
|
||||
default:
|
||||
}
|
||||
var cm v1.ConfigMap
|
||||
commonRefs = make([]CommonReference, 0)
|
||||
if err := ref.Client.Get(ctx, client.ObjectKey{Namespace: capability.Namespace, Name: cmName}, &cm); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
data, ok := cm.Data[types.OpenapiV3JSONSchema]
|
||||
if !ok {
|
||||
return nil, nil, errors.Errorf("configMap doesn't have openapi-v3-json-schema data")
|
||||
}
|
||||
parameterJSON := fmt.Sprintf(BaseOpenAPIV3Template, data)
|
||||
swagger, err := openapi3.NewLoader().LoadFromData(json.RawMessage(parameterJSON))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
parameters := swagger.Components.Schemas[velaprocess.ParameterFieldName].Value
|
||||
WalkParameterSchema(parameters, Specification, 0)
|
||||
|
||||
var consoleRefs []ConsoleReference
|
||||
for _, item := range commonRefs {
|
||||
consoleRefs = append(consoleRefs, ref.prepareConsoleParameter(item.Name, item.Parameters, types.HelmCategory))
|
||||
}
|
||||
return commonRefs, consoleRefs, err
|
||||
}
|
||||
|
||||
// GenerateTerraformCapabilityProperties generates Capability properties for Terraform ComponentDefinition
|
||||
func (ref *ParseReference) parseTerraformCapabilityParameters(capability types.Capability) ([]ReferenceParameterTable, []ReferenceParameterTable, error) {
|
||||
var (
|
||||
|
||||
40
references/docgen/testdata/kube-worker.yaml
vendored
40
references/docgen/testdata/kube-worker.yaml
vendored
@@ -1,40 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: ComponentDefinition
|
||||
metadata:
|
||||
name: kube-worker
|
||||
namespace: default
|
||||
spec:
|
||||
workload:
|
||||
definition:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
schematic:
|
||||
kube:
|
||||
template:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
parameters:
|
||||
- name: image
|
||||
required: true
|
||||
type: string
|
||||
fieldPaths:
|
||||
- "spec.template.spec.containers[0].image"
|
||||
- name: port
|
||||
required: true
|
||||
type: string
|
||||
fieldPaths:
|
||||
- "spec.template.spec.containers[0].ports[0].containerPort"
|
||||
description: "the specific container port num which can accept external request."
|
||||
30
references/docgen/testdata/svcTraitDef.yaml
vendored
30
references/docgen/testdata/svcTraitDef.yaml
vendored
@@ -1,30 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
name: service-kube
|
||||
namespace: default
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
- worker
|
||||
- backend
|
||||
podDisruptive: true
|
||||
schematic:
|
||||
kube:
|
||||
template:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-service
|
||||
spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 9376
|
||||
parameters:
|
||||
- name: targetPort
|
||||
required: true
|
||||
type: number
|
||||
fieldPaths:
|
||||
- "spec.template.spec.ports[0].targetPort"
|
||||
description: "target port num for service provider."
|
||||
@@ -125,35 +125,6 @@ var _ = Describe("Test multicluster scenario", func() {
|
||||
Expect(out).Should(ContainSubstring("alias-worker"))
|
||||
})
|
||||
|
||||
It("Test detach cluster with application use", func() {
|
||||
const testClusterName = "test-cluster"
|
||||
_, err := execCommand("cluster", "join", "/tmp/worker.kubeconfig", "--name", testClusterName)
|
||||
Expect(err).Should(Succeed())
|
||||
app := &v1beta1.Application{}
|
||||
bs, err := os.ReadFile("./testdata/app/example-lite-envbinding-app.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
appYaml := strings.ReplaceAll(string(bs), "TEST_CLUSTER_NAME", testClusterName)
|
||||
Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
|
||||
ctx := context.Background()
|
||||
err = k8sClient.Create(ctx, app)
|
||||
Expect(err).Should(Succeed())
|
||||
namespacedName := client.ObjectKeyFromObject(app)
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Get(ctx, namespacedName, app)).Should(Succeed())
|
||||
g.Expect(len(app.Status.PolicyStatus)).ShouldNot(Equal(0))
|
||||
}, 30*time.Second).Should(Succeed())
|
||||
_, err = execCommand("cluster", "detach", testClusterName)
|
||||
Expect(err).ShouldNot(Succeed())
|
||||
err = k8sClient.Delete(ctx, app)
|
||||
Expect(err).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
err := k8sClient.Get(ctx, namespacedName, app)
|
||||
g.Expect(kerrors.IsNotFound(err)).Should(BeTrue())
|
||||
}, 30*time.Second).Should(Succeed())
|
||||
_, err = execCommand("cluster", "detach", testClusterName)
|
||||
Expect(err).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test generate service account kubeconfig", func() {
|
||||
_, workerCtx := initializeContext()
|
||||
By("create service account kubeconfig in worker cluster")
|
||||
@@ -234,145 +205,6 @@ var _ = Describe("Test multicluster scenario", func() {
|
||||
cleanUpNamespace(hubCtx, workerCtx, prodNamespace)
|
||||
})
|
||||
|
||||
It("Test create EnvBinding Application", func() {
|
||||
// This test is going to cover multiple functions, including
|
||||
// 1. Multiple stage deployment for three environment
|
||||
// 2. Namespace selector.
|
||||
// 3. A special cluster: local cluster
|
||||
// 4. Component selector.
|
||||
By("apply application")
|
||||
app := &v1beta1.Application{}
|
||||
bs, err := os.ReadFile("./testdata/app/example-envbinding-app.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
appYaml := strings.ReplaceAll(strings.ReplaceAll(string(bs), "TEST_NAMESPACE", testNamespace), "PROD_NAMESPACE", prodNamespace)
|
||||
Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
|
||||
app.SetNamespace(namespace)
|
||||
err = k8sClient.Create(hubCtx, app)
|
||||
Expect(err).Should(Succeed())
|
||||
var hubDeployName string
|
||||
By("wait application resource ready")
|
||||
Eventually(func(g Gomega) {
|
||||
// check deployments in clusters
|
||||
deploys := &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(hubCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(1))
|
||||
hubDeployName = deploys.Items[0].Name
|
||||
deploys = &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(namespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(2))
|
||||
deploys = &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(prodNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(2))
|
||||
}, time.Minute).Should(Succeed())
|
||||
Expect(hubDeployName).Should(Equal("data-worker"))
|
||||
// delete application
|
||||
By("delete application")
|
||||
Expect(k8sClient.Delete(hubCtx, app)).Should(Succeed())
|
||||
By("wait application resource delete")
|
||||
Eventually(func(g Gomega) {
|
||||
// check deployments in clusters
|
||||
deploys := &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(hubCtx, deploys, client.InNamespace(namespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(0))
|
||||
deploys = &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(namespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(0))
|
||||
}, time.Minute).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test create EnvBinding Application with trait disable and without workflow, delete env, change env and add env", func() {
|
||||
// This test is going to cover multiple functions, including
|
||||
// 1. disable trait
|
||||
// 2. auto deploy2env workflow
|
||||
// 3. delete env
|
||||
// 4. change cluster in env
|
||||
// 5. add env
|
||||
By("apply application")
|
||||
app := &v1beta1.Application{}
|
||||
bs, err := os.ReadFile("./testdata/app/example-envbinding-app-wo-workflow.yaml")
|
||||
Expect(err).Should(Succeed())
|
||||
appYaml := strings.ReplaceAll(string(bs), "TEST_NAMESPACE", testNamespace)
|
||||
Expect(yaml.Unmarshal([]byte(appYaml), app)).Should(Succeed())
|
||||
app.SetNamespace(testNamespace)
|
||||
namespacedName := client.ObjectKeyFromObject(app)
|
||||
err = k8sClient.Create(hubCtx, app)
|
||||
Expect(err).Should(Succeed())
|
||||
By("wait application resource ready")
|
||||
Eventually(func(g Gomega) {
|
||||
// check deployments in clusters
|
||||
deploys := &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(hubCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(1))
|
||||
g.Expect(int(*deploys.Items[0].Spec.Replicas)).Should(Equal(2))
|
||||
g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(1))
|
||||
g.Expect(int(*deploys.Items[0].Spec.Replicas)).Should(Equal(1))
|
||||
}, time.Minute).Should(Succeed())
|
||||
By("test delete env")
|
||||
spec := &v1alpha1.EnvBindingSpec{}
|
||||
Expect(json.Unmarshal(app.Spec.Policies[0].Properties.Raw, spec)).Should(Succeed())
|
||||
envs := spec.Envs
|
||||
bs, err = json.Marshal(&v1alpha1.EnvBindingSpec{Envs: []v1alpha1.EnvConfig{envs[0]}})
|
||||
Expect(err).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Get(hubCtx, namespacedName, app)).Should(Succeed())
|
||||
app.Spec.Policies[0].Properties.Raw = bs
|
||||
g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
|
||||
}, 15*time.Second).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
deploys := &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(0))
|
||||
}, time.Minute).Should(Succeed())
|
||||
By("test change env cluster name")
|
||||
envs[0].Placement.ClusterSelector.Name = WorkerClusterName
|
||||
bs, err = json.Marshal(&v1alpha1.EnvBindingSpec{Envs: []v1alpha1.EnvConfig{envs[0]}})
|
||||
Expect(err).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Get(hubCtx, namespacedName, app)).Should(Succeed())
|
||||
app.Spec.Policies[0].Properties.Raw = bs
|
||||
g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
|
||||
}, 15*time.Second).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
deploys := &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(hubCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(0))
|
||||
g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(1))
|
||||
}, time.Minute).Should(Succeed())
|
||||
By("test add env")
|
||||
envs[1].Placement.ClusterSelector.Name = multicluster.ClusterLocalName
|
||||
bs, err = json.Marshal(&v1alpha1.EnvBindingSpec{Envs: envs})
|
||||
Expect(err).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
g.Expect(k8sClient.Get(hubCtx, namespacedName, app)).Should(Succeed())
|
||||
app.Spec.Policies[0].Properties.Raw = bs
|
||||
g.Expect(k8sClient.Update(hubCtx, app)).Should(Succeed())
|
||||
}, 15*time.Second).Should(Succeed())
|
||||
Eventually(func(g Gomega) {
|
||||
deploys := &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(hubCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(1))
|
||||
g.Expect(int(*deploys.Items[0].Spec.Replicas)).Should(Equal(1))
|
||||
g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(1))
|
||||
g.Expect(int(*deploys.Items[0].Spec.Replicas)).Should(Equal(2))
|
||||
}, time.Minute).Should(Succeed())
|
||||
// delete application
|
||||
By("delete application")
|
||||
Expect(k8sClient.Delete(hubCtx, app)).Should(Succeed())
|
||||
By("wait application resource delete")
|
||||
Eventually(func(g Gomega) {
|
||||
// check deployments in clusters
|
||||
deploys := &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(hubCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(0))
|
||||
deploys = &appsv1.DeploymentList{}
|
||||
g.Expect(k8sClient.List(workerCtx, deploys, client.InNamespace(testNamespace))).Should(Succeed())
|
||||
g.Expect(len(deploys.Items)).Should(Equal(0))
|
||||
}, time.Minute).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test deploy multi-cluster application with target", func() {
|
||||
By("apply application")
|
||||
app := &v1beta1.Application{
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: example-app
|
||||
namespace: TEST_NAMESPACE # to be replaced
|
||||
spec:
|
||||
components:
|
||||
- name: hello-world-server
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
ports:
|
||||
- port: 8000
|
||||
expose: true
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 2
|
||||
policies:
|
||||
- name: example-multi-env-policy
|
||||
type: env-binding
|
||||
properties:
|
||||
envs:
|
||||
- name: test
|
||||
placement: # selecting the cluster to deploy to
|
||||
clusterSelector:
|
||||
name: local
|
||||
|
||||
- name: staging
|
||||
placement: # selecting the cluster to deploy to
|
||||
clusterSelector:
|
||||
name: cluster-worker
|
||||
patch:
|
||||
components:
|
||||
- name: hello-world-server
|
||||
type: webservice
|
||||
traits:
|
||||
- type: scaler
|
||||
disable: true
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: example-app
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: hello-world-server
|
||||
type: webservice
|
||||
properties:
|
||||
image: crccheck/hello-world
|
||||
ports:
|
||||
- port: 8000
|
||||
expose: true
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 1
|
||||
- name: data-worker
|
||||
type: worker
|
||||
properties:
|
||||
image: busybox
|
||||
cmd:
|
||||
- sleep
|
||||
- '1000000'
|
||||
policies:
|
||||
- name: example-multi-env-policy
|
||||
type: env-binding
|
||||
properties:
|
||||
envs:
|
||||
- name: test
|
||||
placement: # selecting the namespace (in local cluster) to deploy to
|
||||
namespaceSelector:
|
||||
name: TEST_NAMESPACE
|
||||
selector:
|
||||
components:
|
||||
- data-worker
|
||||
|
||||
- name: staging
|
||||
placement: # selecting the cluster to deploy to
|
||||
clusterSelector:
|
||||
name: cluster-worker
|
||||
|
||||
- name: prod
|
||||
placement: # selecting both namespace and cluster to deploy to
|
||||
clusterSelector:
|
||||
name: cluster-worker
|
||||
namespaceSelector:
|
||||
name: PROD_NAMESPACE
|
||||
patch: # overlay patch on above components
|
||||
components:
|
||||
- name: hello-world-server
|
||||
type: webservice
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 3
|
||||
|
||||
workflow:
|
||||
steps:
|
||||
# deploy to test env
|
||||
- name: deploy-test
|
||||
type: deploy2env
|
||||
properties:
|
||||
policy: example-multi-env-policy
|
||||
env: test
|
||||
|
||||
# deploy to staging env
|
||||
- name: deploy-staging
|
||||
type: deploy2env
|
||||
properties:
|
||||
policy: example-multi-env-policy
|
||||
env: staging
|
||||
|
||||
# deploy to prod env
|
||||
- name: deploy-prod
|
||||
type: deploy2env
|
||||
properties:
|
||||
policy: example-multi-env-policy
|
||||
env: prod
|
||||
@@ -1,32 +0,0 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: example-lite-app
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: data-worker
|
||||
type: worker
|
||||
properties:
|
||||
image: busybox
|
||||
cmd:
|
||||
- sleep
|
||||
- '1000000'
|
||||
policies:
|
||||
- name: example-multi-env-policy
|
||||
type: env-binding
|
||||
properties:
|
||||
envs:
|
||||
- name: test
|
||||
placement:
|
||||
clusterSelector:
|
||||
name: TEST_CLUSTER_NAME
|
||||
|
||||
workflow:
|
||||
steps:
|
||||
# deploy to test env
|
||||
- name: deploy-test
|
||||
type: deploy2env
|
||||
properties:
|
||||
policy: example-multi-env-policy
|
||||
env: test
|
||||
@@ -28,10 +28,7 @@ import (
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
@@ -352,339 +349,6 @@ var _ = Describe("Test application of the specified definition version", func()
|
||||
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
PIt("Test deploy application which containing helm module", func() {
|
||||
var (
|
||||
appName = "test-helm"
|
||||
compName = "worker"
|
||||
)
|
||||
|
||||
helmworkerV1 := HELMWorker.DeepCopy()
|
||||
helmworkerV1.SetNamespace(namespace)
|
||||
helmworkerV1.Spec.Workload.Definition = common.WorkloadGVK{
|
||||
APIVersion: "batch/v1beta1",
|
||||
Kind: "CronJob",
|
||||
}
|
||||
helmworkerV1.Spec.Schematic = &common.Schematic{
|
||||
HELM: &common.Helm{
|
||||
Release: *util.Object2RawExtension(map[string]interface{}{
|
||||
"chart": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"chart": "podinfo",
|
||||
"version": "5.1.4",
|
||||
},
|
||||
},
|
||||
}),
|
||||
Repository: *util.Object2RawExtension(map[string]interface{}{
|
||||
"url": "https://stefanprodan.github.io/podinfo",
|
||||
}),
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, helmworkerV1)).Should(Succeed())
|
||||
|
||||
helmworkerV1DefRev := new(v1beta1.DefinitionRevision)
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: "helm-worker-v1", Namespace: namespace}, helmworkerV1DefRev)
|
||||
}, 15*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
helmworkerV2 := new(v1beta1.ComponentDefinition)
|
||||
Eventually(func() error {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: "helm-worker", Namespace: namespace}, helmworkerV2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helmworkerV2.Spec.Workload.Definition = common.WorkloadGVK{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
}
|
||||
helmworkerV2.Spec.Workload.Type = "deployments.apps"
|
||||
helmworkerV2.Spec.Schematic = &common.Schematic{
|
||||
HELM: &common.Helm{
|
||||
Release: *util.Object2RawExtension(map[string]interface{}{
|
||||
"chart": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"chart": "podinfo",
|
||||
"version": "5.2.0",
|
||||
},
|
||||
},
|
||||
}),
|
||||
Repository: *util.Object2RawExtension(map[string]interface{}{
|
||||
"url": "https://stefanprodan.github.io/podinfo",
|
||||
}),
|
||||
},
|
||||
}
|
||||
return k8sClient.Update(ctx, helmworkerV2)
|
||||
}, 15*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
helmworkerV2DefRev := new(v1beta1.DefinitionRevision)
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: "helm-worker-v2", Namespace: namespace}, helmworkerV2DefRev)
|
||||
}, 15*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
app := v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: "helm-worker",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "5.1.2",
|
||||
},
|
||||
}),
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "label",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"hello": "world",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
By("Create application")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, app.DeepCopy())
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully by Helm")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := fmt.Sprintf("%s-%s-podinfo", appName, compName)
|
||||
Eventually(func() bool {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
DeployLabels := deploy.GetLabels()
|
||||
return DeployLabels["helm.sh/chart"] == "podinfo-5.2.0"
|
||||
}, 120*time.Second, 5*time.Second).Should(BeTrue())
|
||||
|
||||
By("Verify trait is applied to the workload")
|
||||
Eventually(func() bool {
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
|
||||
return false
|
||||
}
|
||||
By("Verify patch trait is applied")
|
||||
templateLabels := deploy.GetLabels()
|
||||
return templateLabels["hello"] == "world"
|
||||
}, 200*time.Second, 10*time.Second).Should(BeTrue())
|
||||
|
||||
app = v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: "helm-worker@v1",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "5.1.2",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
By("Create application")
|
||||
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is update successfully by Helm")
|
||||
deploy = &appsv1.Deployment{}
|
||||
Eventually(func() bool {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
DeployLabels := deploy.GetLabels()
|
||||
return DeployLabels["helm.sh/chart"] != "podinfo-5.1.4"
|
||||
}, 120*time.Second, 5*time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Test deploy application which containing kube module", func() {
|
||||
var (
|
||||
appName = "test-kube-app"
|
||||
compName = "worker"
|
||||
)
|
||||
|
||||
kubeworkerV1 := KUBEWorker.DeepCopy()
|
||||
kubeworkerV1.Spec.Workload.Definition = common.WorkloadGVK{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
}
|
||||
kubeworkerV1.Spec.Schematic = &common.Schematic{
|
||||
KUBE: &common.Kube{
|
||||
Template: generateTemplate(KUBEWorkerV1Template),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
Required: pointer.Bool(true),
|
||||
Description: pointer.String("test description"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
kubeworkerV1.SetNamespace(namespace)
|
||||
Expect(k8sClient.Create(ctx, kubeworkerV1)).Should(Succeed())
|
||||
|
||||
kubeworkerV1DefRev := new(v1beta1.DefinitionRevision)
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: "kube-worker-v1", Namespace: namespace}, kubeworkerV1DefRev)
|
||||
}, 15*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
kubeworkerV2 := new(v1beta1.ComponentDefinition)
|
||||
Eventually(func() error {
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Name: "kube-worker", Namespace: namespace}, kubeworkerV2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kubeworkerV2.Spec.Workload.Definition = common.WorkloadGVK{
|
||||
APIVersion: "batch/v1",
|
||||
Kind: "Job",
|
||||
}
|
||||
kubeworkerV2.Spec.Workload.Type = "jobs.batch"
|
||||
kubeworkerV2.Spec.Schematic = &common.Schematic{
|
||||
KUBE: &common.Kube{
|
||||
Template: generateTemplate(KUBEWorkerV2Template),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
Required: pointer.Bool(true),
|
||||
Description: pointer.String("test description"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return k8sClient.Update(ctx, kubeworkerV2)
|
||||
}, 15*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
kubeworkerV2DefRev := new(v1beta1.DefinitionRevision)
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: "kube-worker-v2", Namespace: namespace}, kubeworkerV2DefRev)
|
||||
}, 15*time.Second, time.Second).Should(BeNil())
|
||||
|
||||
app := v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: "kube-worker",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": "busybox",
|
||||
}),
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "label",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"hello": "world",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
By("Create application")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, app.DeepCopy())
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Verify the workload(job) is created successfully")
|
||||
job := &batchv1.Job{}
|
||||
jobName := compName
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: jobName, Namespace: namespace}, job)
|
||||
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
||||
|
||||
By("Verify trait is applied to the workload")
|
||||
Labels := job.GetLabels()
|
||||
Expect(Labels["hello"]).Should(Equal("world"))
|
||||
|
||||
By("Update Application and Specify the Definition version in Application")
|
||||
app = v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: "kube-worker@v1",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": "nginx",
|
||||
}),
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "label",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"labels": map[string]string{
|
||||
"hello": "kubevela",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := compName
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
||||
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
||||
|
||||
By("Verify trait is applied to the workload")
|
||||
webserviceV1Labels := deploy.GetLabels()
|
||||
Expect(webserviceV1Labels["hello"]).Should(Equal("kubevela"))
|
||||
|
||||
By("Application specifies the wrong version of the Definition, it will raise an error")
|
||||
app = v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: "kube-worker@a1",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": "nginx",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(HaveOccurred())
|
||||
})
|
||||
|
||||
// refer to https://github.com/oam-dev/kubevela/discussions/1810#discussioncomment-914295
|
||||
It("Test k8s resources created by application whether with correct label", func() {
|
||||
var (
|
||||
@@ -892,11 +556,6 @@ var exposeWithNoTemplate = &v1beta1.TraitDefinition{
|
||||
},
|
||||
}
|
||||
|
||||
func generateTemplate(template string) runtime.RawExtension {
|
||||
b, _ := yaml.YAMLToJSON([]byte(template))
|
||||
return runtime.RawExtension{Raw: b}
|
||||
}
|
||||
|
||||
var webServiceV1Template = `output: {
|
||||
apiVersion: "apps/v1"
|
||||
kind: "Deployment"
|
||||
|
||||
@@ -1,387 +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 controllers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
|
||||
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"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Test application containing helm module", func() {
|
||||
ctx := context.Background()
|
||||
var (
|
||||
appName = "test-app"
|
||||
compName = "test-comp"
|
||||
cdName = "webapp-chart"
|
||||
wdName = "webapp-chart-wd"
|
||||
tdName = "virtualgroup"
|
||||
)
|
||||
var namespace string
|
||||
var app v1beta1.Application
|
||||
var ns corev1.Namespace
|
||||
|
||||
BeforeEach(func() {
|
||||
namespace = randomNamespaceName("helm-e2e-test")
|
||||
ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
|
||||
Eventually(
|
||||
func() error {
|
||||
return k8sClient.Create(ctx, &ns)
|
||||
},
|
||||
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
cd := v1beta1.ComponentDefinition{}
|
||||
cd.SetName(cdName)
|
||||
cd.SetNamespace(namespace)
|
||||
cd.Spec.Workload.Definition = common.WorkloadGVK{APIVersion: "apps/v1", Kind: "Deployment"}
|
||||
cd.Spec.Schematic = &common.Schematic{
|
||||
HELM: &common.Helm{
|
||||
Release: *util.Object2RawExtension(map[string]interface{}{
|
||||
"chart": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"chart": "podinfo",
|
||||
"version": "5.1.4",
|
||||
},
|
||||
},
|
||||
}),
|
||||
Repository: *util.Object2RawExtension(map[string]interface{}{
|
||||
"url": "https://charts.kubevela.net/example/",
|
||||
}),
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, &cd)).Should(Succeed())
|
||||
|
||||
By("Install a patch trait used to test CUE module")
|
||||
td := v1beta1.TraitDefinition{}
|
||||
td.SetName(tdName)
|
||||
td.SetNamespace(namespace)
|
||||
td.Spec.AppliesToWorkloads = []string{"deployments.apps"}
|
||||
td.Spec.Schematic = &common.Schematic{
|
||||
CUE: &common.CUE{
|
||||
Template: `patch: {
|
||||
spec: template: {
|
||||
metadata: labels: {
|
||||
if parameter.type == "namespace" {
|
||||
"app.namespace.virtual.group": parameter.group
|
||||
}
|
||||
if parameter.type == "cluster" {
|
||||
"app.cluster.virtual.group": parameter.group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
group: *"default" | string
|
||||
type: *"namespace" | string
|
||||
}`,
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, &td)).Should(Succeed())
|
||||
|
||||
By("Add 'deployments.apps' to scaler's appliesToWorkloads")
|
||||
scalerTd := v1beta1.TraitDefinition{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "scaler", Namespace: "vela-system"}, &scalerTd)).Should(Succeed())
|
||||
scalerTd.Spec.AppliesToWorkloads = []string{"deployments.apps", "webservice", "worker"}
|
||||
scalerTd.SetResourceVersion("")
|
||||
Expect(k8sClient.Patch(ctx, &scalerTd, client.Merge)).Should(Succeed())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
By("Clean up resources after a test")
|
||||
|
||||
k8sClient.DeleteAllOf(ctx, &v1beta1.Application{}, client.InNamespace(namespace))
|
||||
k8sClient.DeleteAllOf(ctx, &v1beta1.ComponentDefinition{}, client.InNamespace(namespace))
|
||||
k8sClient.DeleteAllOf(ctx, &v1beta1.WorkloadDefinition{}, client.InNamespace(namespace))
|
||||
k8sClient.DeleteAllOf(ctx, &v1beta1.TraitDefinition{}, client.InNamespace(namespace))
|
||||
By(fmt.Sprintf("Delete the entire namespaceName %s", ns.Name))
|
||||
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed())
|
||||
By("Remove 'deployments.apps' from scaler's appliesToWorkloads")
|
||||
scalerTd := v1beta1.TraitDefinition{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "scaler", Namespace: "vela-system"}, &scalerTd)).Should(Succeed())
|
||||
scalerTd.Spec.AppliesToWorkloads = []string{"webservice", "worker"}
|
||||
scalerTd.SetResourceVersion("")
|
||||
Expect(k8sClient.Patch(ctx, &scalerTd, client.Merge)).Should(Succeed())
|
||||
})
|
||||
|
||||
PIt("Test deploy an application containing helm module", func() {
|
||||
app = v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: cdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "5.1.2",
|
||||
},
|
||||
}),
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "scaler",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"replicas": 0,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Type: tdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"group": "my-group",
|
||||
"type": "cluster",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Create application")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, app.DeepCopy())
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully by Helm")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := fmt.Sprintf("%s-%s-podinfo", appName, compName)
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
||||
}, 120*time.Second, 5*time.Second).Should(Succeed())
|
||||
|
||||
By("Verify two traits are applied to the workload")
|
||||
Eventually(func() bool {
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
|
||||
return false
|
||||
}
|
||||
By("Verify patch trait is applied")
|
||||
templateLabels := deploy.Spec.Template.Labels
|
||||
if templateLabels["app.cluster.virtual.group"] != "my-group" {
|
||||
return false
|
||||
}
|
||||
By("Verify scaler trait is applied")
|
||||
if *deploy.Spec.Replicas != 0 {
|
||||
return false
|
||||
}
|
||||
By("Verify application's settings override chart default values")
|
||||
// the default value of 'image.tag' is 5.1.4 in the chart, but settings reset it to 5.1.2
|
||||
return strings.HasSuffix(deploy.Spec.Template.Spec.Containers[0].Image, "5.1.2")
|
||||
// it takes pretty long time to fetch chart and install the Helm release
|
||||
}, 120*time.Second, 10*time.Second).Should(BeTrue())
|
||||
|
||||
By("Update the application")
|
||||
app = v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: cdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "5.1.3", // change 5.1.2 => 5.1.3
|
||||
},
|
||||
}),
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "scaler",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"replicas": 1, // change 0 => 1
|
||||
}),
|
||||
},
|
||||
{
|
||||
Type: tdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"group": "my-group-0", // change my-group => my-group-0
|
||||
"type": "cluster",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
|
||||
|
||||
By("Verify the changes are applied to the workload")
|
||||
Eventually(func() bool {
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
|
||||
return false
|
||||
}
|
||||
By("Verify new patch trait is applied")
|
||||
templateLabels := deploy.Spec.Template.Labels
|
||||
if templateLabels["app.cluster.virtual.group"] != "my-group-0" {
|
||||
return false
|
||||
}
|
||||
By("Verify new scaler trait is applied")
|
||||
if *deploy.Spec.Replicas != 1 {
|
||||
return false
|
||||
}
|
||||
By("Verify new application's settings override chart default values")
|
||||
return strings.HasSuffix(deploy.Spec.Template.Spec.Containers[0].Image, "5.1.3")
|
||||
}, 120*time.Second, 10*time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Test deploy an application containing helm module defined by workloadDefinition", func() {
|
||||
|
||||
workloaddef := v1beta1.WorkloadDefinition{}
|
||||
workloaddef.SetName(wdName)
|
||||
workloaddef.SetNamespace(namespace)
|
||||
workloaddef.Spec.Reference = common.DefinitionReference{Name: "deployments.apps", Version: "v1"}
|
||||
workloaddef.Spec.Schematic = &common.Schematic{
|
||||
HELM: &common.Helm{
|
||||
Release: *util.Object2RawExtension(map[string]interface{}{
|
||||
"chart": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"chart": "podinfo",
|
||||
"version": "5.1.4",
|
||||
},
|
||||
},
|
||||
}),
|
||||
Repository: *util.Object2RawExtension(map[string]interface{}{
|
||||
"url": "https://charts.kubevela.net/example/",
|
||||
}),
|
||||
},
|
||||
}
|
||||
By("register workloadDefinition")
|
||||
Expect(k8sClient.Create(ctx, &workloaddef)).Should(Succeed())
|
||||
|
||||
appTestName := "test-app-refer-to-workloaddef"
|
||||
appTest := v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appTestName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: wdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "5.1.2",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Create application")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, appTest.DeepCopy())
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully by Helm")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := fmt.Sprintf("%s-%s-podinfo", appTestName, compName)
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
||||
}, 240*time.Second, 5*time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test deploy an application containing helm module and the component refer to autodetect type workload", func() {
|
||||
cd := v1beta1.ComponentDefinition{}
|
||||
cd.SetName("podinfo")
|
||||
cd.SetNamespace(namespace)
|
||||
cd.Spec.Schematic = &common.Schematic{
|
||||
HELM: &common.Helm{
|
||||
Release: *util.Object2RawExtension(map[string]interface{}{
|
||||
"chart": map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"chart": "podinfo",
|
||||
"version": "5.1.4",
|
||||
},
|
||||
},
|
||||
}),
|
||||
Repository: *util.Object2RawExtension(map[string]interface{}{
|
||||
"url": "https://charts.kubevela.net/example/",
|
||||
}),
|
||||
},
|
||||
}
|
||||
cd.Spec.Workload.Type = types.AutoDetectWorkloadDefinition
|
||||
Expect(k8sClient.Create(ctx, &cd)).Should(Succeed())
|
||||
|
||||
newAppName := "test-autodetect"
|
||||
newApp := v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: newAppName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: "podinfo",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": map[string]interface{}{
|
||||
"tag": "5.1.2",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Create application")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, newApp.DeepCopy())
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully by Helm")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := fmt.Sprintf("%s-%s-podinfo", newAppName, compName)
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
||||
}, 120*time.Second, 5*time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test store JSON schema of Helm Chart in ConfigMap", func() {
|
||||
By("Get the ConfigMap")
|
||||
cmName := fmt.Sprintf("component-schema-%s", cdName)
|
||||
Eventually(func() error {
|
||||
cm := &corev1.ConfigMap{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: cmName, Namespace: namespace}, cm); err != nil {
|
||||
return err
|
||||
}
|
||||
if cm.Data[types.OpenapiV3JSONSchema] == "" {
|
||||
return errors.New("json schema is not found in the ConfigMap")
|
||||
}
|
||||
return nil
|
||||
}, 60*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
})
|
||||
})
|
||||
@@ -1,357 +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 controllers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Test application containing kube module", func() {
|
||||
ctx := context.Background()
|
||||
var (
|
||||
appName = "test-app"
|
||||
compName = "test-comp"
|
||||
cdName = "test-kube-worker"
|
||||
wdName = "test-kube-worker-wd"
|
||||
tdName = "test-virtualgroup"
|
||||
)
|
||||
var namespace string
|
||||
var app v1beta1.Application
|
||||
var ns corev1.Namespace
|
||||
|
||||
var testTemplate = func() runtime.RawExtension {
|
||||
yamlStr := `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
ports:
|
||||
- containerPort: 80 `
|
||||
b, _ := yaml.YAMLToJSON([]byte(yamlStr))
|
||||
return runtime.RawExtension{Raw: b}
|
||||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
namespace = randomNamespaceName("kube-e2e-test")
|
||||
ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
|
||||
Eventually(
|
||||
func() error {
|
||||
return k8sClient.Create(ctx, &ns)
|
||||
},
|
||||
time.Second*3, time.Millisecond*300).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
|
||||
cd := v1beta1.ComponentDefinition{}
|
||||
cd.SetName(cdName)
|
||||
cd.SetNamespace(namespace)
|
||||
cd.Spec.Workload.Definition = common.WorkloadGVK{APIVersion: "apps/v1", Kind: "Deployment"}
|
||||
cd.Spec.Schematic = &common.Schematic{
|
||||
KUBE: &common.Kube{
|
||||
Template: testTemplate(),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
Required: pointer.Bool(true),
|
||||
Description: pointer.String("test description"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, &cd)).Should(Succeed())
|
||||
|
||||
By("Install a patch trait used to test CUE module")
|
||||
td := v1beta1.TraitDefinition{}
|
||||
td.SetName(tdName)
|
||||
td.SetNamespace(namespace)
|
||||
td.Spec.AppliesToWorkloads = []string{"deployments.apps"}
|
||||
td.Spec.Schematic = &common.Schematic{
|
||||
CUE: &common.CUE{
|
||||
Template: `patch: {
|
||||
spec: template: {
|
||||
metadata: labels: {
|
||||
if parameter.type == "namespace" {
|
||||
"app.namespace.virtual.group": parameter.group
|
||||
}
|
||||
if parameter.type == "cluster" {
|
||||
"app.cluster.virtual.group": parameter.group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
group: *"default" | string
|
||||
type: *"namespace" | string
|
||||
}`,
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, &td)).Should(Succeed())
|
||||
|
||||
By("Verify ComponentDefinition and TraitDefinition are created successfully")
|
||||
Eventually(func() error {
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: cdName, Namespace: namespace}, &v1beta1.ComponentDefinition{}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: tdName, Namespace: namespace}, &v1beta1.TraitDefinition{}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}, 20*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Add 'deployments.apps' to scaler's appliesToWorkloads")
|
||||
scalerTd := v1beta1.TraitDefinition{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "scaler", Namespace: "vela-system"}, &scalerTd)).Should(Succeed())
|
||||
scalerTd.Spec.AppliesToWorkloads = []string{"deployments.apps", "webservice", "worker"}
|
||||
scalerTd.SetResourceVersion("")
|
||||
Expect(k8sClient.Patch(ctx, &scalerTd, client.Merge)).Should(Succeed())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
By("Clean up resources after a test")
|
||||
k8sClient.DeleteAllOf(ctx, &v1beta1.Application{}, client.InNamespace(namespace))
|
||||
k8sClient.DeleteAllOf(ctx, &v1beta1.ComponentDefinition{}, client.InNamespace(namespace))
|
||||
k8sClient.DeleteAllOf(ctx, &v1beta1.WorkloadDefinition{}, client.InNamespace(namespace))
|
||||
k8sClient.DeleteAllOf(ctx, &v1beta1.TraitDefinition{}, client.InNamespace(namespace))
|
||||
By(fmt.Sprintf("Delete the entire namespaceName %s", ns.Name))
|
||||
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed())
|
||||
|
||||
By("Remove 'deployments.apps' from scaler's appliesToWorkloads")
|
||||
scalerTd := v1beta1.TraitDefinition{}
|
||||
Expect(k8sClient.Get(ctx, client.ObjectKey{Name: "scaler", Namespace: "vela-system"}, &scalerTd)).Should(Succeed())
|
||||
scalerTd.Spec.AppliesToWorkloads = []string{"webservice", "worker"}
|
||||
scalerTd.SetResourceVersion("")
|
||||
Expect(k8sClient.Patch(ctx, &scalerTd, client.Merge)).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test deploy an application containing kube module", func() {
|
||||
app = v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: cdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": "nginx:1.14.0",
|
||||
}),
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "scaler",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"replicas": 2,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Type: tdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"group": "my-group",
|
||||
"type": "cluster",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Create application")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, app.DeepCopy())
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := compName
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
||||
}, 30*time.Second, 3*time.Second).Should(Succeed())
|
||||
|
||||
By("Verify two traits are applied to the workload")
|
||||
Eventually(func() bool {
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
|
||||
return false
|
||||
}
|
||||
By("Verify patch trait is applied")
|
||||
templateLabels := deploy.Spec.Template.Labels
|
||||
if templateLabels["app.cluster.virtual.group"] != "my-group" {
|
||||
return false
|
||||
}
|
||||
By("Verify scaler trait is applied")
|
||||
if *deploy.Spec.Replicas != 2 {
|
||||
return false
|
||||
}
|
||||
By("Verify parameter is applied")
|
||||
return deploy.Spec.Template.Spec.Containers[0].Image == "nginx:1.14.0"
|
||||
}, 15*time.Second, 3*time.Second).Should(BeTrue())
|
||||
|
||||
By("Update the application")
|
||||
app = v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: cdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": "nginx:1.14.1", // nginx:1.14.0 => nginx:1.14.1
|
||||
}),
|
||||
Traits: []common.ApplicationTrait{
|
||||
{
|
||||
Type: "scaler",
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"replicas": 3, // change 2 => 3
|
||||
}),
|
||||
},
|
||||
{
|
||||
Type: tdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"group": "my-group-0", // change my-group => my-group-0
|
||||
"type": "cluster",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Patch(ctx, &app, client.Merge)).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully")
|
||||
deploy = &appsv1.Deployment{}
|
||||
deployName = compName
|
||||
|
||||
By("Verify the changes are applied to the workload")
|
||||
Eventually(func() bool {
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
|
||||
return false
|
||||
}
|
||||
By("Verify new patch trait is applied")
|
||||
templateLabels := deploy.Spec.Template.Labels
|
||||
if templateLabels["app.cluster.virtual.group"] != "my-group-0" {
|
||||
return false
|
||||
}
|
||||
By("Verify new scaler trait is applied")
|
||||
if *deploy.Spec.Replicas != 3 {
|
||||
return false
|
||||
}
|
||||
By("Verify new parameter is applied")
|
||||
return deploy.Spec.Template.Spec.Containers[0].Image == "nginx:1.14.1"
|
||||
}, 60*time.Second, 10*time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Test deploy an application containing kube module defined by workloadDefinition", func() {
|
||||
workloaddef := v1beta1.WorkloadDefinition{}
|
||||
workloaddef.SetName(wdName)
|
||||
workloaddef.SetNamespace(namespace)
|
||||
workloaddef.Spec.Reference = common.DefinitionReference{Name: "deployments.apps", Version: "v1"}
|
||||
workloaddef.Spec.Schematic = &common.Schematic{
|
||||
KUBE: &common.Kube{
|
||||
Template: testTemplate(),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
Required: pointer.Bool(true),
|
||||
Description: pointer.String("test description"),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("register workloadDefinition")
|
||||
Expect(k8sClient.Create(ctx, &workloaddef)).Should(Succeed())
|
||||
|
||||
appTestName := "test-app-refer-to-workloaddef"
|
||||
appTest := v1beta1.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appTestName,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: v1beta1.ApplicationSpec{
|
||||
Components: []common.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: cdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": "nginx:1.14.0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Create application")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Create(ctx, appTest.DeepCopy())
|
||||
}, 10*time.Second, 500*time.Millisecond).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := compName
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy)
|
||||
}, 15*time.Second, 3*time.Second).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test store JSON schema of Kube parameter in ConfigMap", func() {
|
||||
By("Get the ConfigMap")
|
||||
cmName := fmt.Sprintf("component-schema-%s", cdName)
|
||||
Eventually(func() error {
|
||||
cm := &corev1.ConfigMap{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: cmName, Namespace: namespace}, cm); err != nil {
|
||||
return err
|
||||
}
|
||||
if cm.Data[types.OpenapiV3JSONSchema] == "" {
|
||||
return errors.New("json schema is not found in the ConfigMap")
|
||||
}
|
||||
return nil
|
||||
}, 60*time.Second, 5*time.Second).Should(Succeed())
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user