mirror of
https://github.com/kubevela/kubevela.git
synced 2026-02-14 18:10:21 +00:00
implemente kube schematic (#1298)
* implemente kube schematic generate schema for kube schematic parameters add e2e test Signed-off-by: roywang <seiwy2010@gmail.com> * add unit test Signed-off-by: roy wang <seiwy2010@gmail.com> * add doc Signed-off-by: roy wang <seiwy2010@gmail.com>
This commit is contained in:
@@ -23,6 +23,51 @@ import (
|
||||
"github.com/oam-dev/kubevela/apis/standard.oam.dev/v1alpha1"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// data types of parameter value
|
||||
const (
|
||||
StringType ParameterValueType = "string"
|
||||
NumberType ParameterValueType = "number"
|
||||
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.
|
||||
@@ -33,11 +78,13 @@ 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"`
|
||||
|
||||
// TODO(wonderflow): support HCL(terraform)/KUBE(K8s Object) here.
|
||||
// TODO(wonderflow): support HCL(terraform)here.
|
||||
}
|
||||
|
||||
// A Helm represents resources used by a Helm module
|
||||
|
||||
@@ -161,6 +161,59 @@ func (in *Helm) DeepCopy() *Helm {
|
||||
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 *RawComponent) DeepCopyInto(out *RawComponent) {
|
||||
*out = *in
|
||||
@@ -195,6 +248,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)
|
||||
|
||||
@@ -101,11 +101,15 @@ const (
|
||||
// CapabilityCategory defines the category of a capability
|
||||
type CapabilityCategory string
|
||||
|
||||
// categories of capability schematic
|
||||
const (
|
||||
// TerraformCategory means the capability is in Terraform format
|
||||
TerraformCategory CapabilityCategory = "terraform"
|
||||
// HelmCategory means the capability is a helm capability
|
||||
|
||||
HelmCategory CapabilityCategory = "helm"
|
||||
|
||||
KubeCategory CapabilityCategory = "kube"
|
||||
|
||||
CUECategory CapabilityCategory = "cue"
|
||||
)
|
||||
|
||||
// Parameter defines a parameter for cli from capability template
|
||||
|
||||
@@ -518,6 +518,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
@@ -715,6 +758,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for trait
|
||||
@@ -853,6 +939,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
@@ -1412,6 +1541,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
@@ -1610,6 +1782,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for trait
|
||||
@@ -1748,6 +1963,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
|
||||
@@ -91,6 +91,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
@@ -237,6 +280,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
|
||||
@@ -93,6 +93,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for trait
|
||||
@@ -224,6 +267,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for trait
|
||||
|
||||
@@ -107,6 +107,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
@@ -247,6 +290,49 @@ spec:
|
||||
- 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
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
- [Define Components in Chart](/en/helm/component.md)
|
||||
- [Attach Traits](/en/helm/trait.md)
|
||||
- [Known Limitations](/en/helm/known-issues.md)
|
||||
|
||||
- Using Raw Kube
|
||||
- [Define Components With Raw K8s](/en/kube/component.md)
|
||||
- [Attach Traits](/en/kube/trait.md)
|
||||
|
||||
- Developer Experience Guide
|
||||
- Appfile
|
||||
|
||||
@@ -64,7 +64,7 @@ spec:
|
||||
tag: "5.1.2"
|
||||
```
|
||||
|
||||
Helm module workload will use data in `settings` as [Helm chart values](https://github.com/captainroy-hy/podinfo/blob/master/charts/podinfo/values.yaml).
|
||||
Helm module workload will use data in `properties` as [Helm chart values](https://github.com/captainroy-hy/podinfo/blob/master/charts/podinfo/values.yaml).
|
||||
You can learn the schema of settings by reading the `README.md` of the Helm
|
||||
chart, and the schema are totally align with
|
||||
[`values.yaml`](https://github.com/captainroy-hy/podinfo/blob/master/charts/podinfo/values.yaml)
|
||||
|
||||
110
docs/en/kube/component.md
Normal file
110
docs/en/kube/component.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Use Raw Kubernetes Resource To Extend a Component type
|
||||
|
||||
This documentation explains how to use raw K8s resource to define an application component.
|
||||
|
||||
Before reading this part, please make sure you've learned [the definition and template concepts](../platform-engineers/definition-and-templates.md).
|
||||
|
||||
## Write ComponentDefinition
|
||||
|
||||
Here is an example `ComponentDefinition` about how to use raw k8s resource as schematic module.
|
||||
|
||||
```yaml
|
||||
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"
|
||||
```
|
||||
|
||||
Just like using CUE as schematic module, we also have some rules and contracts
|
||||
to use raw k8s resource as schematic module.
|
||||
`.spec.schematic.kube` contains template of the raw k8s resource and
|
||||
configurable parameters.
|
||||
|
||||
- `.spec.schematic.kube.template` is the raw k8s resource in YAML format just like
|
||||
we usually defined in a YAML file.
|
||||
|
||||
- `.spec.schematic.kube.parameters` contains a set of configurable parameters.
|
||||
`name`, `type`, and `fieldPaths` are required fields.
|
||||
`description` and `required` are optional fields.
|
||||
|
||||
- The parameter `name` must be unique in a `ComponentDefinition`.
|
||||
|
||||
- `type` indicates the data type of value set to the field in a workload.
|
||||
This is a required field which will help Vela to generate a OpenAPI JSON schema
|
||||
for the parameters automatically.
|
||||
Currently, only basic data types are allowed, including `string`, `number`, and
|
||||
`boolean`, while `array` and `object` are not.
|
||||
|
||||
- `fieldPaths` in the parameter 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`, `spec.containers[0].image`.
|
||||
|
||||
## Create an Application using Kube schematic ComponentDefinition
|
||||
|
||||
Here is an example `Application`.
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: myapp
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: mycomp
|
||||
type: kube-worker
|
||||
properties:
|
||||
image: nginx:1.14.0
|
||||
```
|
||||
|
||||
Kube schematic workload will use data in `properties` as the values of
|
||||
parameters.
|
||||
Since parameters only support basic data type, values in `properties` should be
|
||||
formatted as simple key-value, `<parameterName>: <parameterValue>`.
|
||||
And don't forget to set value to required parameter.
|
||||
|
||||
Deploy the `Application` and verify the resulting workload.
|
||||
|
||||
```shell
|
||||
$ kubectl get deploy
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
mycomp-v1 1/1 1 1 66m
|
||||
```
|
||||
And check the parameter works.
|
||||
```shell
|
||||
$ kubectl get deployment mycomp-v1 -o json | jq '.spec.template.spec.containers[0].image'
|
||||
"nginx:1.14.0"
|
||||
```
|
||||
|
||||
108
docs/en/kube/trait.md
Normal file
108
docs/en/kube/trait.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Attach Traits to Kube Based Components
|
||||
|
||||
Most traits in KubeVela can be attached to Kube based component seamlessly.
|
||||
In this sample application below, we add two traits,
|
||||
[scaler](https://github.com/oam-dev/kubevela/blob/master/charts/vela-core/templates/defwithtemplate/manualscale.yaml)
|
||||
and
|
||||
[virtualgroup](https://github.com/oam-dev/kubevela/blob/master/docs/examples/kube-module/virtual-group-td.yaml), to a Kube based component.
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: myapp
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: mycomp
|
||||
type: kube-worker
|
||||
properties:
|
||||
image: nginx:1.14.0
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 2
|
||||
- type: virtualgroup
|
||||
properties:
|
||||
group: "my-group1"
|
||||
type: "cluster"
|
||||
```
|
||||
|
||||
## Verify traits work correctly
|
||||
|
||||
Deploy the application and verify traits work.
|
||||
|
||||
Check the scaler trait.
|
||||
```shell
|
||||
$ kubectl get manualscalertrait
|
||||
NAME AGE
|
||||
demo-podinfo-scaler-3x1sfcd34 2m
|
||||
```
|
||||
```shell
|
||||
$ kubectl get deployment mycomp-v1 -o json | jq .spec.replicas
|
||||
2
|
||||
```
|
||||
|
||||
Check the virtualgroup trait.
|
||||
```shell
|
||||
$ kubectl get deployment mycomp-v1 -o json | jq .spec.template.metadata.labels
|
||||
{
|
||||
"app.cluster.virtual.group": "my-group1",
|
||||
"app.kubernetes.io/name": "myapp"
|
||||
}
|
||||
```
|
||||
|
||||
## Update an Application
|
||||
|
||||
After the application is deployed and workloads/traits are created successfully,
|
||||
you can update the application, and corresponding changes will be applied to the
|
||||
workload.
|
||||
|
||||
Let's make several changes on the configuration of the sample application.
|
||||
|
||||
```yaml
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: myapp
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: mycomp
|
||||
type: kube-worker
|
||||
properties:
|
||||
image: nginx:1.14.1 # 1.14.0 => 1.14.1
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 4 # 2 => 4
|
||||
- type: virtualgroup
|
||||
properties:
|
||||
group: "my-group2" # my-group1 => my-group2
|
||||
type: "cluster"
|
||||
```
|
||||
|
||||
Apply the new configuration and check the results after several seconds.
|
||||
|
||||
> After updating, the workload name is changed from `mycomp-v1` to `mycomp-v2`.
|
||||
|
||||
Check the new parameter works.
|
||||
```shell
|
||||
$ kubectl get deployment mycomp-v2 -o json | jq '.spec.template.spec.containers[0].image'
|
||||
"nginx:1.14.1"
|
||||
```
|
||||
|
||||
Check the scaler trait.
|
||||
```shell
|
||||
$ kubectl get deployment mycomp-v2 -o json | jq .spec.replicas
|
||||
4
|
||||
```
|
||||
|
||||
Check the virtualgroup trait.
|
||||
```shell
|
||||
$ kubectl get deployment mycomp-v2 -o json | jq .spec.template.metadata.labels
|
||||
{
|
||||
"app.cluster.virtual.group": "my-group2",
|
||||
"app.kubernetes.io/name": "myapp"
|
||||
}
|
||||
```
|
||||
19
docs/examples/kube-module/kube-app.yaml
Normal file
19
docs/examples/kube-module/kube-app.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: myapp
|
||||
namespace: default
|
||||
spec:
|
||||
components:
|
||||
- name: mycomp
|
||||
type: kube-worker
|
||||
properties:
|
||||
image: nginx:1.14.0
|
||||
traits:
|
||||
- type: scaler
|
||||
properties:
|
||||
replicas: 2
|
||||
- type: virtualgroup
|
||||
properties:
|
||||
group: "my-group1"
|
||||
type: "cluster"
|
||||
34
docs/examples/kube-module/kube-worker-cd.yaml
Normal file
34
docs/examples/kube-module/kube-worker-cd.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
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"
|
||||
29
docs/examples/kube-module/virtual-group-td.yaml
Normal file
29
docs/examples/kube-module/virtual-group-td.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
apiVersion: core.oam.dev/v1beta1
|
||||
kind: TraitDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
definition.oam.dev/description: "Add virtual group labels"
|
||||
name: virtualgroup
|
||||
spec:
|
||||
appliesToWorkloads:
|
||||
- webservice
|
||||
- worker
|
||||
- deployments.apps
|
||||
extension:
|
||||
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
|
||||
}
|
||||
@@ -516,6 +516,49 @@ spec:
|
||||
- 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:
|
||||
|
||||
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
|
||||
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
@@ -714,6 +757,49 @@ spec:
|
||||
- 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:
|
||||
|
||||
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
|
||||
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for trait
|
||||
@@ -852,6 +938,49 @@ spec:
|
||||
- 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:
|
||||
|
||||
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
|
||||
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
|
||||
@@ -91,6 +91,49 @@ spec:
|
||||
- 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:
|
||||
|
||||
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
|
||||
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
|
||||
@@ -93,6 +93,49 @@ spec:
|
||||
- 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:
|
||||
|
||||
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
|
||||
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for trait
|
||||
|
||||
@@ -106,6 +106,49 @@ spec:
|
||||
- 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:
|
||||
|
||||
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
|
||||
|
||||
required:
|
||||
- template
|
||||
type: object
|
||||
type: object
|
||||
status:
|
||||
description: Status defines the custom health policy and status message for workload
|
||||
|
||||
204
pkg/appfile/kube_parameter_test.go
Normal file
204
pkg/appfile/kube_parameter_test.go
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
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 appfile
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/crossplane/crossplane-runtime/pkg/test"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
)
|
||||
|
||||
func TestResolveKubeParameters(t *testing.T) {
|
||||
stringParam := &common.KubeParameter{
|
||||
Name: "strParam",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec"},
|
||||
}
|
||||
requiredParam := &common.KubeParameter{
|
||||
Name: "reqParam",
|
||||
Required: pointer.BoolPtr(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
|
||||
obj unstructured.Unstructured
|
||||
values paramValueSettings
|
||||
wantObj unstructured.Unstructured
|
||||
wantErr error
|
||||
}{
|
||||
"InvalidStringType": {
|
||||
reason: "An error should be returned",
|
||||
values: paramValueSettings{
|
||||
"strParam": paramValueSetting{
|
||||
Value: int32(100),
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec.test"},
|
||||
},
|
||||
},
|
||||
wantErr: errors.Errorf(errInvalidValueType, common.StringType),
|
||||
},
|
||||
"InvalidNumberType": {
|
||||
reason: "An error should be returned",
|
||||
values: paramValueSettings{
|
||||
"intParam": paramValueSetting{
|
||||
Value: "test",
|
||||
ValueType: common.NumberType,
|
||||
FieldPaths: []string{"spec.test"},
|
||||
},
|
||||
},
|
||||
wantErr: errors.Errorf(errInvalidValueType, common.NumberType),
|
||||
},
|
||||
"InvalidBoolType": {
|
||||
reason: "An error should be returned",
|
||||
values: paramValueSettings{
|
||||
"boolParam": paramValueSetting{
|
||||
Value: "test",
|
||||
ValueType: common.BooleanType,
|
||||
FieldPaths: []string{"spec.test"},
|
||||
},
|
||||
},
|
||||
wantErr: errors.Errorf(errInvalidValueType, common.BooleanType),
|
||||
},
|
||||
"InvalidFieldPath": {
|
||||
reason: "An error should be returned",
|
||||
values: paramValueSettings{
|
||||
"strParam": paramValueSetting{
|
||||
Value: "test",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec[.test"}, // a invalid field path
|
||||
},
|
||||
},
|
||||
wantErr: errors.Wrap(errors.New(`cannot parse path "spec[.test": unterminated '[' at position 4`),
|
||||
`cannot set parameter "strParam" to field "spec[.test"`),
|
||||
},
|
||||
"Succeed": {
|
||||
reason: "No error should be returned",
|
||||
obj: unstructured.Unstructured{Object: make(map[string]interface{})},
|
||||
values: paramValueSettings{
|
||||
"strParam": paramValueSetting{
|
||||
Value: "test",
|
||||
ValueType: common.StringType,
|
||||
FieldPaths: []string{"spec.strField"},
|
||||
},
|
||||
"intParam": paramValueSetting{
|
||||
Value: 10,
|
||||
ValueType: common.NumberType,
|
||||
FieldPaths: []string{"spec.intField"},
|
||||
},
|
||||
"floatParam": paramValueSetting{
|
||||
Value: float64(10.01),
|
||||
ValueType: common.NumberType,
|
||||
FieldPaths: []string{"spec.floatField"},
|
||||
},
|
||||
"boolParam": paramValueSetting{
|
||||
Value: true,
|
||||
ValueType: common.BooleanType,
|
||||
FieldPaths: []string{"spec.boolField"},
|
||||
},
|
||||
},
|
||||
wantObj: unstructured.Unstructured{Object: map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"strField": "test",
|
||||
"intField": int64(10),
|
||||
"floatField": float64(10.01),
|
||||
"boolField": true,
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for tcName, tc := range tests {
|
||||
t.Run(tcName, func(t *testing.T) {
|
||||
obj := tc.obj.DeepCopy()
|
||||
err := setParameterValuesToKubeObj(obj, tc.values)
|
||||
if diff := cmp.Diff(tc.wantObj, *obj); diff != "" {
|
||||
t.Errorf("\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
|
||||
}
|
||||
if diff := cmp.Diff(tc.wantErr, err, test.EquateErrors()); diff != "" {
|
||||
t.Errorf("\nsetParameterValuesToKubeObj(...)error -want +get \nreason:%s\n%s\n", tc.reason, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,9 +21,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/format"
|
||||
json2cue "cuelang.org/go/encoding/json"
|
||||
"github.com/crossplane/crossplane-runtime/apis/core/v1alpha1"
|
||||
"github.com/crossplane/crossplane-runtime/pkg/fieldpath"
|
||||
"github.com/pkg/errors"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"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"
|
||||
@@ -46,6 +51,11 @@ const (
|
||||
AppfileBuiltinConfig = "config"
|
||||
)
|
||||
|
||||
// constant error information
|
||||
const (
|
||||
errInvalidValueType = "require %q type parameter value"
|
||||
)
|
||||
|
||||
// Workload is component
|
||||
type Workload struct {
|
||||
Name string
|
||||
@@ -271,6 +281,11 @@ func (p *Parser) GenerateApplicationConfiguration(app *Appfile, ns string) (*v1a
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
case types.KubeCategory:
|
||||
comp, acComp, err = generateComponentFromKubeModule(p.client, wl, app.Name, app.RevisionName, ns)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
comp, acComp, err = generateComponentFromCUEModule(p.client, wl, app.Name, app.RevisionName, ns)
|
||||
if err != nil {
|
||||
@@ -320,6 +335,124 @@ func generateComponentFromCUEModule(c client.Client, wl *Workload, appName, revi
|
||||
return comp, acComp, nil
|
||||
}
|
||||
|
||||
func generateComponentFromKubeModule(c client.Client, wl *Workload, appName, revision, ns string) (*v1alpha2.Component, *v1alpha2.ApplicationConfigurationComponent, error) {
|
||||
kubeObj := &unstructured.Unstructured{}
|
||||
err := json.Unmarshal(wl.FullTemplate.Kube.Template.Raw, kubeObj)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot decode Kube template into K8s object")
|
||||
}
|
||||
|
||||
paramValues, err := resolveKubeParameters(wl.FullTemplate.Kube.Parameters, wl.Params)
|
||||
if err != nil {
|
||||
return nil, nil, errors.WithMessage(err, "cannot resolve parameter settings")
|
||||
}
|
||||
if err := setParameterValuesToKubeObj(kubeObj, paramValues); err != nil {
|
||||
return nil, nil, 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 nil, nil, errors.Wrap(err, "cannot marshal kube object")
|
||||
}
|
||||
ins, err := json2cue.Decode(&cue.Runtime{}, "", objRaw)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot decode object into CUE")
|
||||
}
|
||||
cueRaw, err := format.Node(ins.Value().Syntax())
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot format CUE")
|
||||
}
|
||||
|
||||
// NOTE a hack way to enable using CUE capabilities on KUBE schematic workload
|
||||
wl.Template = fmt.Sprintf(`
|
||||
output: {
|
||||
%s
|
||||
}`, string(cueRaw))
|
||||
|
||||
// re-use the way CUE module generates comp & acComp
|
||||
comp, acComp, err := generateComponentFromCUEModule(c, wl, appName, revision, ns)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return comp, acComp, nil
|
||||
}
|
||||
|
||||
// a helper map whose key is parameter name
|
||||
type paramValueSettings map[string]paramValueSetting
|
||||
type paramValueSetting struct {
|
||||
Value interface{}
|
||||
ValueType common.ParameterValueType
|
||||
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 {
|
||||
for _, f := range v.FieldPaths {
|
||||
switch v.ValueType {
|
||||
case common.StringType:
|
||||
vString, ok := v.Value.(string)
|
||||
if !ok {
|
||||
return errors.Errorf(errInvalidValueType, v.ValueType)
|
||||
}
|
||||
if err := paved.SetString(f, vString); err != nil {
|
||||
return errors.Wrapf(err, "cannot set parameter %q to field %q", paramName, f)
|
||||
}
|
||||
case common.NumberType:
|
||||
switch v.Value.(type) {
|
||||
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||||
if err := paved.SetValue(f, v.Value); err != nil {
|
||||
return errors.Wrapf(err, "cannot set parameter %q to field %q", paramName, f)
|
||||
}
|
||||
default:
|
||||
return errors.Errorf(errInvalidValueType, v.ValueType)
|
||||
}
|
||||
case common.BooleanType:
|
||||
vBoolean, ok := v.Value.(bool)
|
||||
if !ok {
|
||||
return errors.Errorf(errInvalidValueType, v.ValueType)
|
||||
}
|
||||
if err := paved.SetValue(f, vBoolean); err != nil {
|
||||
return errors.Wrapf(err, "cannot set parameter %q to field %q", paramName, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateComponentFromHelmModule(c client.Client, wl *Workload, appName, revision, ns string) (*v1alpha2.Component, *v1alpha2.ApplicationConfigurationComponent, error) {
|
||||
gv, err := schema.ParseGroupVersion(wl.DefinitionReference.APIVersion)
|
||||
if err != nil {
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -34,6 +35,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
@@ -669,3 +671,180 @@ var _ = Describe("Test appfile parser to parse helm module", func() {
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
var _ = Describe("Test appfile parser to parse kube module", 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 expectWorkload = func() runtime.RawExtension {
|
||||
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 `
|
||||
b, _ := yaml.YAMLToJSON([]byte(yamlStr))
|
||||
return runtime.RawExtension{Raw: b}
|
||||
}
|
||||
var testAppfile = func() *Appfile {
|
||||
return &Appfile{
|
||||
Name: appName,
|
||||
Workloads: []*Workload{
|
||||
{
|
||||
Name: compName,
|
||||
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),
|
||||
Template: `
|
||||
outputs: scaler: {
|
||||
apiVersion: "core.oam.dev/v1alpha2"
|
||||
kind: "ManualScalerTrait"
|
||||
spec: {
|
||||
replicaCount: parameter.replicas
|
||||
}
|
||||
}
|
||||
parameter: {
|
||||
//+short=r
|
||||
replicas: *1 | int
|
||||
}
|
||||
`,
|
||||
},
|
||||
},
|
||||
FullTemplate: &util.Template{
|
||||
Kube: &common.Kube{
|
||||
Template: testTemplate(),
|
||||
Parameters: []common.KubeParameter{
|
||||
{
|
||||
Name: "image",
|
||||
ValueType: common.StringType,
|
||||
Required: pointer.BoolPtr(true),
|
||||
FieldPaths: []string{"spec.template.spec.containers[0].image"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DefinitionReference: common.WorkloadGVK{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
manuscaler := util.Object2RawExtension(&unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "core.oam.dev/v1alpha2",
|
||||
"kind": "ManualScalerTrait",
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app.oam.dev/component": compName,
|
||||
"app.oam.dev/name": appName,
|
||||
"trait.oam.dev/type": "scaler",
|
||||
"trait.oam.dev/resource": "scaler",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{"replicaCount": int64(10)},
|
||||
},
|
||||
})
|
||||
|
||||
It("Test application containing kube module", func() {
|
||||
By("Generate ApplicationConfiguration and Components")
|
||||
ac, components, err := NewApplicationParser(k8sClient, dm, pd).GenerateApplicationConfiguration(testAppfile(), "default")
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
expectAppConfig := &v1alpha2.ApplicationConfiguration{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "ApplicationConfiguration",
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
}, ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{oam.LabelAppName: appName},
|
||||
},
|
||||
Spec: v1alpha2.ApplicationConfigurationSpec{
|
||||
Components: []v1alpha2.ApplicationConfigurationComponent{
|
||||
{
|
||||
ComponentName: compName,
|
||||
Traits: []v1alpha2.ComponentTrait{
|
||||
{
|
||||
Trait: manuscaler,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
expectComponent := &v1alpha2.Component{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Component",
|
||||
APIVersion: "core.oam.dev/v1alpha2",
|
||||
}, ObjectMeta: metav1.ObjectMeta{
|
||||
Name: compName,
|
||||
Namespace: "default",
|
||||
Labels: map[string]string{oam.LabelAppName: appName},
|
||||
},
|
||||
Spec: v1alpha2.ComponentSpec{
|
||||
Workload: expectWorkload(),
|
||||
},
|
||||
}
|
||||
By("Verify expected ApplicationConfiguration")
|
||||
diff := cmp.Diff(ac, expectAppConfig)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
By("Verify expected Component")
|
||||
diff = cmp.Diff(components[0], expectComponent)
|
||||
Expect(diff).ShouldNot(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test missing set required parameter", func() {
|
||||
appfile := testAppfile()
|
||||
// remove parameter settings
|
||||
appfile.Workloads[0].Params = nil
|
||||
_, _, err := NewApplicationParser(k8sClient, dm, pd).GenerateApplicationConfiguration(appfile, "default")
|
||||
|
||||
expectError := errors.WithMessage(errors.New(`require parameter "image"`), "cannot resolve parameter settings")
|
||||
diff := cmp.Diff(expectError, err, test.EquateErrors())
|
||||
Expect(diff).Should(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -86,12 +86,14 @@ func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
|
||||
var def utils.CapabilityComponentDefinition
|
||||
def.Name = req.NamespacedName.Name
|
||||
def.WorkloadType = workloadType
|
||||
def.ComponentDefinition = componentDefinition
|
||||
switch workloadType {
|
||||
case util.ReferWorkload:
|
||||
def.WorkloadDefName = componentDefinition.Spec.Workload.Type
|
||||
case util.HELMDef:
|
||||
def.Helm = componentDefinition.Spec.Schematic.HELM
|
||||
def.ComponentDefinition = componentDefinition
|
||||
case util.KubeDef:
|
||||
def.Kube = componentDefinition.Spec.Schematic.KUBE
|
||||
default:
|
||||
}
|
||||
err = def.StoreOpenAPISchema(ctx, r, req.Namespace, req.Name)
|
||||
|
||||
@@ -48,6 +48,9 @@ func (h *handler) CreateWorkloadDefinition(ctx context.Context) (util.WorkloadTy
|
||||
if h.cd.Spec.Schematic != nil && h.cd.Spec.Schematic.HELM != nil {
|
||||
workloadType = util.HELMDef
|
||||
}
|
||||
if h.cd.Spec.Schematic != nil && h.cd.Spec.Schematic.KUBE != nil {
|
||||
workloadType = util.KubeDef
|
||||
}
|
||||
|
||||
wd := new(v1alpha2.WorkloadDefinition)
|
||||
err := h.Get(ctx, client.ObjectKey{Namespace: h.cd.Namespace, Name: workloadName}, wd)
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/pkg/errors"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -62,6 +63,7 @@ type CapabilityComponentDefinition struct {
|
||||
WorkloadDefName string `json:"workloadDefName"`
|
||||
|
||||
Helm *commontypes.Helm `json:"helm"`
|
||||
Kube *commontypes.Kube `json:"kube"`
|
||||
CapabilityBaseDefinition
|
||||
}
|
||||
|
||||
@@ -105,6 +107,43 @@ func (def *CapabilityComponentDefinition) GetOpenAPISchema(ctx context.Context,
|
||||
return getOpenAPISchema(*capability)
|
||||
}
|
||||
|
||||
// GetKubeSchematicOpenAPISchema gets OpenAPI v3 schema based on kube schematic parameters
|
||||
func (def *CapabilityComponentDefinition) 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)
|
||||
}
|
||||
// save FieldPaths into description
|
||||
tmp.Description = fmt.Sprintf("The value will be applied to fields: [%s].", strings.Join(p.FieldPaths, ","))
|
||||
if p.Description != nil {
|
||||
tmp.Description = fmt.Sprintf("%s\n %s", tmp.Description, *p.Description)
|
||||
}
|
||||
properties[p.Name] = tmp
|
||||
}
|
||||
s := openapi3.NewObjectSchema().WithProperties(properties)
|
||||
if len(required) > 0 {
|
||||
s.Required = required
|
||||
}
|
||||
b, err := s.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot marshal generated schema into json")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// StoreOpenAPISchema stores OpenAPI v3 schema in ConfigMap from WorkloadDefinition
|
||||
func (def *CapabilityComponentDefinition) StoreOpenAPISchema(ctx context.Context, k8sClient client.Client, namespace, name string) error {
|
||||
var jsonSchema []byte
|
||||
@@ -112,6 +151,8 @@ func (def *CapabilityComponentDefinition) StoreOpenAPISchema(ctx context.Context
|
||||
switch def.WorkloadType {
|
||||
case util.HELMDef:
|
||||
jsonSchema, err = helm.GetChartValuesJSONSchema(ctx, def.Helm)
|
||||
case util.KubeDef:
|
||||
jsonSchema, err = def.GetKubeSchematicOpenAPISchema(def.Kube.Parameters)
|
||||
default:
|
||||
jsonSchema, err = def.GetOpenAPISchema(ctx, k8sClient, namespace, name)
|
||||
}
|
||||
|
||||
@@ -106,6 +106,9 @@ const (
|
||||
// ComponentDef describe a workload of Defined by ComponentDefinition
|
||||
ComponentDef WorkloadType = "ComponentDef"
|
||||
|
||||
// KubeDef describe a workload refer to raw K8s resource
|
||||
KubeDef WorkloadType = "KubeDef"
|
||||
|
||||
// HELMDef describe a workload refer to HELM
|
||||
HELMDef WorkloadType = "HelmDef"
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ type Template struct {
|
||||
CapabilityCategory types.CapabilityCategory
|
||||
Reference common.WorkloadGVK
|
||||
Helm *common.Helm
|
||||
Kube *common.Kube
|
||||
// TODO: Add scope definition too
|
||||
ComponentDefinition *v1beta1.ComponentDefinition
|
||||
WorkloadDefinition *v1beta1.WorkloadDefinition
|
||||
@@ -159,6 +160,10 @@ func NewTemplate(schematic *common.Schematic, status *common.Status, raw *runtim
|
||||
tmp.CapabilityCategory = types.HelmCategory
|
||||
return tmp, nil
|
||||
}
|
||||
if schematic.KUBE != nil {
|
||||
tmp.Kube = schematic.KUBE
|
||||
tmp.CapabilityCategory = types.KubeCategory
|
||||
}
|
||||
}
|
||||
|
||||
extension := map[string]interface{}{}
|
||||
|
||||
@@ -155,7 +155,7 @@ var _ = Describe("AppConfig renders workloads", func() {
|
||||
|
||||
By("Verify workloads are created")
|
||||
Eventually(func() bool {
|
||||
reconcileAppConfigNow(ctx, ac)
|
||||
requestReconcileNow(ctx, ac)
|
||||
cw := &v1alpha2.ContainerizedWorkload{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: cwName, Namespace: namespace}, cw); err != nil {
|
||||
return false
|
||||
|
||||
@@ -312,7 +312,7 @@ var _ = Describe("Versioning mechanism of components", func() {
|
||||
By("Check ContainerizedWorkload workload's image field has been changed to v2")
|
||||
cwWlV2 := &v1alpha2.ContainerizedWorkload{}
|
||||
Eventually(func() string {
|
||||
reconcileAppConfigNow(ctx, &appConfigWithRevisionName)
|
||||
requestReconcileNow(ctx, &appConfigWithRevisionName)
|
||||
k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, cwWlV2)
|
||||
return cwWlV2.Spec.Containers[0].Image
|
||||
}, time.Second*60, time.Microsecond*500).Should(Equal(imageV2))
|
||||
@@ -358,7 +358,7 @@ var _ = Describe("Versioning mechanism of components", func() {
|
||||
var w1 unstructured.Unstructured
|
||||
Eventually(
|
||||
func() error {
|
||||
reconcileAppConfigNow(ctx, &appconfig)
|
||||
requestReconcileNow(ctx, &appconfig)
|
||||
w1.SetAPIVersion("example.com/v1")
|
||||
w1.SetKind("Bar")
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revisionNameV1}, &w1)
|
||||
@@ -390,7 +390,7 @@ var _ = Describe("Versioning mechanism of components", func() {
|
||||
var w2 unstructured.Unstructured
|
||||
Eventually(
|
||||
func() error {
|
||||
reconcileAppConfigNow(ctx, &appconfig)
|
||||
requestReconcileNow(ctx, &appconfig)
|
||||
w2.SetAPIVersion("example.com/v1")
|
||||
w2.SetKind("Bar")
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: revisionNameV2}, &w2)
|
||||
@@ -477,7 +477,7 @@ var _ = Describe("Versioning mechanism of components", func() {
|
||||
var w2 unstructured.Unstructured
|
||||
Eventually(
|
||||
func() string {
|
||||
reconcileAppConfigNow(ctx, &appconfig)
|
||||
requestReconcileNow(ctx, &appconfig)
|
||||
w2.SetAPIVersion("example.com/v1")
|
||||
w2.SetKind("Bar")
|
||||
err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: componentName}, &w2)
|
||||
|
||||
@@ -317,7 +317,7 @@ var _ = Describe("HealthScope", func() {
|
||||
By("Verify health scope")
|
||||
Eventually(
|
||||
func() v1alpha2.ScopeHealthCondition {
|
||||
reconcileAppConfigNow(ctx, &appConfig)
|
||||
requestReconcileNow(ctx, &appConfig)
|
||||
*healthScope = v1alpha2.HealthScope{}
|
||||
k8sClient.Get(ctx, healthScopeObject, healthScope)
|
||||
logf.Log.Info("Checking on health scope",
|
||||
|
||||
@@ -144,19 +144,6 @@ var _ = Describe("Test application containing helm module", func() {
|
||||
Expect(k8sClient.Patch(ctx, &scalerTd, client.Merge)).Should(Succeed())
|
||||
})
|
||||
|
||||
// reconcileAppConfigNow will trigger an immediate reconciliation on AppConfig.
|
||||
// Some test cases may fail for timeout to wait a scheduled reconciliation.
|
||||
// This is a workaround to avoid long-time wait before next scheduled
|
||||
// reconciliation.
|
||||
reconcileAppContextNow := func(ctx context.Context, ac *v1alpha2.ApplicationContext) error {
|
||||
u := ac.DeepCopy()
|
||||
u.SetAnnotations(map[string]string{
|
||||
"app.oam.dev/requestreconcile": time.Now().String(),
|
||||
})
|
||||
u.SetResourceVersion("")
|
||||
return k8sClient.Patch(ctx, u, client.Merge)
|
||||
}
|
||||
|
||||
It("Test deploy an application containing helm module", func() {
|
||||
app = v1alpha2.Application{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -211,9 +198,7 @@ var _ = Describe("Test application containing helm module", func() {
|
||||
|
||||
By("Verify two traits are applied to the workload")
|
||||
Eventually(func() bool {
|
||||
if err := reconcileAppContextNow(ctx, ac); err != nil {
|
||||
return false
|
||||
}
|
||||
requestReconcileNow(ctx, ac)
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
|
||||
return false
|
||||
@@ -282,9 +267,7 @@ var _ = Describe("Test application containing helm module", func() {
|
||||
|
||||
By("Verify the changes are applied to the workload")
|
||||
Eventually(func() bool {
|
||||
if err := reconcileAppContextNow(ctx, ac); err != nil {
|
||||
return false
|
||||
}
|
||||
requestReconcileNow(ctx, ac)
|
||||
deploy := &appsv1.Deployment{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: deployName, Namespace: namespace}, deploy); err != nil {
|
||||
return false
|
||||
|
||||
377
test/e2e-test/kube_app_test.go
Normal file
377
test/e2e-test/kube_app_test.go
Normal file
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
Copyright 2020 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"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha2"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Test application containing kube module", func() {
|
||||
ctx := context.Background()
|
||||
var (
|
||||
namespace = "kube-test-ns"
|
||||
appName = "test-app"
|
||||
compName = "test-comp"
|
||||
cdName = "test-kube-worker"
|
||||
wdName = "test-kube-worker-wd"
|
||||
tdName = "test-virtualgroup"
|
||||
)
|
||||
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() {
|
||||
ns = corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
|
||||
Eventually(
|
||||
func() error {
|
||||
return k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))
|
||||
},
|
||||
time.Second*120, time.Millisecond*500).Should(SatisfyAny(BeNil(), &util.NotFoundMatcher{}))
|
||||
By("make sure all the resources are removed")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: namespace}, &corev1.Namespace{})
|
||||
}, time.Second*120, time.Millisecond*500).Should(&util.NotFoundMatcher{})
|
||||
By("create test 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.BoolPtr(true),
|
||||
Description: pointer.StringPtr("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("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))
|
||||
Expect(k8sClient.Delete(ctx, &ns, client.PropagationPolicy(metav1.DeletePropagationForeground))).Should(Succeed())
|
||||
By("make sure all the resources are removed")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: namespace}, &corev1.Namespace{})
|
||||
}, time.Second*120, time.Millisecond*500).Should(&util.NotFoundMatcher{})
|
||||
|
||||
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: []v1beta1.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: cdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": "nginx:1.14.0",
|
||||
}),
|
||||
Traits: []v1beta1.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")
|
||||
Expect(k8sClient.Create(ctx, &app)).Should(Succeed())
|
||||
|
||||
ac := &v1alpha2.ApplicationContext{}
|
||||
acName := appName
|
||||
By("Verify the ApplicationContext is created successfully")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac)
|
||||
}, 30*time.Second, time.Second).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := fmt.Sprintf("%s-v1", 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 {
|
||||
requestReconcileNow(ctx, ac)
|
||||
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: []v1beta1.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: []v1beta1.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 ApplicationContext is updated")
|
||||
deploy = &appsv1.Deployment{}
|
||||
Eventually(func() bool {
|
||||
ac = &v1alpha2.ApplicationContext{}
|
||||
if err := k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac); err != nil {
|
||||
return false
|
||||
}
|
||||
return ac.GetGeneration() == 2
|
||||
}, 15*time.Second, 3*time.Second).Should(BeTrue())
|
||||
|
||||
By("Verify the changes are applied to the workload")
|
||||
deployName = fmt.Sprintf("%s-v2", compName)
|
||||
Eventually(func() bool {
|
||||
requestReconcileNow(ctx, ac)
|
||||
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.BoolPtr(true),
|
||||
Description: pointer.StringPtr("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: []v1beta1.ApplicationComponent{
|
||||
{
|
||||
Name: compName,
|
||||
Type: cdName,
|
||||
Properties: util.Object2RawExtension(map[string]interface{}{
|
||||
"image": "nginx:1.14.0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
By("Create application")
|
||||
Expect(k8sClient.Create(ctx, &appTest)).Should(Succeed())
|
||||
|
||||
ac := &v1alpha2.ApplicationContext{}
|
||||
acName := appTestName
|
||||
By("Verify the AppConfig is created successfully")
|
||||
Eventually(func() error {
|
||||
return k8sClient.Get(ctx, client.ObjectKey{Name: acName, Namespace: namespace}, ac)
|
||||
}, 30*time.Second, time.Second).Should(Succeed())
|
||||
|
||||
By("Verify the workload(deployment) is created successfully")
|
||||
deploy := &appsv1.Deployment{}
|
||||
deployName := fmt.Sprintf("%s-v1", 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("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["openapi-v3-json-schema"] == "" {
|
||||
return errors.New("json schema is not found in the ConfigMap")
|
||||
}
|
||||
return nil
|
||||
}, 60*time.Second, 5*time.Second).Should(Succeed())
|
||||
})
|
||||
})
|
||||
@@ -19,6 +19,7 @@ package controllers_test
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -294,15 +295,19 @@ var _ = AfterSuite(func() {
|
||||
Expect(k8sClient.Delete(context.Background(), &crd)).Should(BeNil())
|
||||
})
|
||||
|
||||
// reconcileAppConfigNow will trigger an immediate reconciliation on AppConfig.
|
||||
// requestReconcileNow will trigger an immediate reconciliation on K8s object.
|
||||
// Some test cases may fail for timeout to wait a scheduled reconciliation.
|
||||
// This is a workaround to avoid long-time wait before next scheduled
|
||||
// reconciliation.
|
||||
func reconcileAppConfigNow(ctx context.Context, ac *v1alpha2.ApplicationConfiguration) error {
|
||||
u := ac.DeepCopy()
|
||||
u.SetAnnotations(map[string]string{
|
||||
func requestReconcileNow(ctx context.Context, o runtime.Object) {
|
||||
By(fmt.Sprintf("Requset reconcile %q now",
|
||||
o.GetObjectKind().GroupVersionKind().String()))
|
||||
oCopy := o.DeepCopyObject()
|
||||
oMeta, ok := oCopy.(metav1.Object)
|
||||
Expect(ok).Should(BeTrue())
|
||||
oMeta.SetAnnotations(map[string]string{
|
||||
"app.oam.dev/requestreconcile": time.Now().String(),
|
||||
})
|
||||
u.SetResourceVersion("")
|
||||
return k8sClient.Patch(ctx, u, client.Merge)
|
||||
oMeta.SetResourceVersion("")
|
||||
Expect(k8sClient.Patch(ctx, oCopy, client.Merge)).Should(Succeed())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user